grape 1.1.0 → 1.5.3
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 +278 -44
- data/LICENSE +1 -1
- data/README.md +514 -69
- data/UPGRADING.md +424 -17
- data/grape.gemspec +13 -2
- data/lib/grape.rb +104 -71
- data/lib/grape/api.rb +138 -175
- data/lib/grape/api/helpers.rb +2 -0
- data/lib/grape/api/instance.rb +283 -0
- data/lib/grape/config.rb +34 -0
- data/lib/grape/content_types.rb +34 -0
- data/lib/grape/cookies.rb +2 -0
- data/lib/grape/dsl/api.rb +2 -0
- data/lib/grape/dsl/callbacks.rb +22 -0
- data/lib/grape/dsl/configuration.rb +2 -0
- data/lib/grape/dsl/desc.rb +41 -7
- data/lib/grape/dsl/headers.rb +2 -0
- data/lib/grape/dsl/helpers.rb +5 -2
- data/lib/grape/dsl/inside_route.rb +92 -49
- data/lib/grape/dsl/logger.rb +2 -0
- data/lib/grape/dsl/middleware.rb +9 -0
- data/lib/grape/dsl/parameters.rb +25 -14
- data/lib/grape/dsl/request_response.rb +4 -2
- data/lib/grape/dsl/routing.rb +17 -10
- data/lib/grape/dsl/settings.rb +7 -1
- data/lib/grape/dsl/validations.rb +24 -4
- data/lib/grape/eager_load.rb +20 -0
- data/lib/grape/endpoint.rb +59 -35
- data/lib/grape/error_formatter.rb +4 -2
- data/lib/grape/error_formatter/base.rb +2 -0
- data/lib/grape/error_formatter/json.rb +2 -0
- data/lib/grape/error_formatter/txt.rb +2 -0
- data/lib/grape/error_formatter/xml.rb +2 -0
- data/lib/grape/exceptions/base.rb +20 -14
- 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 +2 -0
- data/lib/grape/exceptions/missing_mime_type.rb +2 -0
- data/lib/grape/exceptions/missing_option.rb +2 -0
- data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
- data/lib/grape/exceptions/unknown_options.rb +2 -0
- data/lib/grape/exceptions/unknown_parameter.rb +2 -0
- data/lib/grape/exceptions/unknown_validator.rb +2 -0
- data/lib/grape/exceptions/unsupported_group_type.rb +2 -0
- data/lib/grape/exceptions/validation.rb +4 -2
- 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.rb +5 -3
- data/lib/grape/formatter/json.rb +2 -0
- data/lib/grape/formatter/serializable_hash.rb +2 -0
- data/lib/grape/formatter/txt.rb +2 -0
- data/lib/grape/formatter/xml.rb +2 -0
- data/lib/grape/http/headers.rb +50 -18
- data/lib/grape/locale/en.yml +3 -1
- data/lib/grape/middleware/auth/base.rb +7 -7
- data/lib/grape/middleware/auth/dsl.rb +2 -0
- data/lib/grape/middleware/auth/strategies.rb +2 -0
- data/lib/grape/middleware/auth/strategy_info.rb +2 -0
- data/lib/grape/middleware/base.rb +10 -7
- data/lib/grape/middleware/error.rb +21 -16
- data/lib/grape/middleware/filter.rb +2 -0
- data/lib/grape/middleware/formatter.rb +8 -6
- data/lib/grape/middleware/globals.rb +2 -0
- data/lib/grape/middleware/helpers.rb +12 -0
- data/lib/grape/middleware/stack.rb +13 -3
- data/lib/grape/middleware/versioner.rb +2 -0
- data/lib/grape/middleware/versioner/accept_version_header.rb +2 -0
- data/lib/grape/middleware/versioner/header.rb +10 -8
- data/lib/grape/middleware/versioner/param.rb +3 -1
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +4 -1
- data/lib/grape/middleware/versioner/path.rb +3 -1
- data/lib/grape/namespace.rb +14 -2
- data/lib/grape/parser.rb +4 -2
- data/lib/grape/parser/json.rb +3 -1
- data/lib/grape/parser/xml.rb +3 -1
- data/lib/grape/path.rb +15 -3
- data/lib/grape/presenters/presenter.rb +2 -0
- data/lib/grape/request.rb +19 -10
- data/lib/grape/router.rb +30 -29
- data/lib/grape/router/attribute_translator.rb +41 -8
- data/lib/grape/router/pattern.rb +20 -16
- data/lib/grape/router/route.rb +14 -28
- data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
- data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
- data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
- data/lib/grape/util/base_inheritable.rb +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 +2 -0
- data/lib/grape/util/inheritable_values.rb +7 -25
- data/lib/grape/util/json.rb +2 -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 +98 -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 +2 -0
- data/lib/grape/util/xml.rb +2 -0
- data/lib/grape/validations.rb +2 -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 +51 -30
- data/lib/grape/validations/single_attribute_iterator.rb +24 -0
- data/lib/grape/validations/types.rb +13 -38
- data/lib/grape/validations/types/array_coercer.rb +65 -0
- data/lib/grape/validations/types/build_coercer.rb +47 -49
- data/lib/grape/validations/types/custom_type_coercer.rb +29 -51
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
- data/lib/grape/validations/types/dry_type_coercer.rb +76 -0
- data/lib/grape/validations/types/file.rb +22 -18
- data/lib/grape/validations/types/invalid_value.rb +24 -0
- data/lib/grape/validations/types/json.rb +46 -39
- data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
- data/lib/grape/validations/types/primitive_coercer.rb +67 -0
- data/lib/grape/validations/types/set_coercer.rb +40 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
- data/lib/grape/validations/validator_factory.rb +8 -11
- data/lib/grape/validations/validators/all_or_none.rb +8 -13
- data/lib/grape/validations/validators/allow_blank.rb +3 -1
- data/lib/grape/validations/validators/as.rb +5 -4
- data/lib/grape/validations/validators/at_least_one_of.rb +7 -13
- data/lib/grape/validations/validators/base.rb +20 -16
- data/lib/grape/validations/validators/coerce.rb +46 -29
- data/lib/grape/validations/validators/default.rb +6 -6
- data/lib/grape/validations/validators/exactly_one_of.rb +10 -23
- data/lib/grape/validations/validators/except_values.rb +4 -2
- data/lib/grape/validations/validators/multiple_params_base.rb +17 -10
- data/lib/grape/validations/validators/mutual_exclusion.rb +8 -18
- data/lib/grape/validations/validators/presence.rb +3 -1
- data/lib/grape/validations/validators/regexp.rb +4 -2
- data/lib/grape/validations/validators/same_as.rb +26 -0
- data/lib/grape/validations/validators/values.rb +18 -6
- data/lib/grape/version.rb +3 -1
- data/spec/grape/api/custom_validations_spec.rb +5 -3
- data/spec/grape/api/deeply_included_options_spec.rb +2 -0
- data/spec/grape/api/defines_boolean_in_params_spec.rb +39 -0
- data/spec/grape/api/inherited_helpers_spec.rb +2 -0
- data/spec/grape/api/instance_spec.rb +104 -0
- data/spec/grape/api/invalid_format_spec.rb +2 -0
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +2 -0
- data/spec/grape/api/nested_helpers_spec.rb +2 -0
- data/spec/grape/api/optional_parameters_in_route_spec.rb +2 -0
- data/spec/grape/api/parameters_modification_spec.rb +3 -1
- data/spec/grape/api/patch_method_helpers_spec.rb +2 -0
- data/spec/grape/api/recognize_path_spec.rb +2 -0
- data/spec/grape/api/required_parameters_in_route_spec.rb +2 -0
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +2 -0
- data/spec/grape/api/routes_with_requirements_spec.rb +61 -0
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +2 -0
- data/spec/grape/api/shared_helpers_spec.rb +2 -0
- data/spec/grape/api_remount_spec.rb +473 -0
- data/spec/grape/api_spec.rb +565 -12
- data/spec/grape/config_spec.rb +19 -0
- data/spec/grape/dsl/callbacks_spec.rb +2 -0
- data/spec/grape/dsl/configuration_spec.rb +2 -0
- data/spec/grape/dsl/desc_spec.rb +42 -16
- data/spec/grape/dsl/headers_spec.rb +2 -0
- data/spec/grape/dsl/helpers_spec.rb +4 -2
- data/spec/grape/dsl/inside_route_spec.rb +184 -33
- data/spec/grape/dsl/logger_spec.rb +2 -0
- data/spec/grape/dsl/middleware_spec.rb +10 -0
- data/spec/grape/dsl/parameters_spec.rb +2 -0
- data/spec/grape/dsl/request_response_spec.rb +2 -0
- data/spec/grape/dsl/routing_spec.rb +12 -0
- data/spec/grape/dsl/settings_spec.rb +2 -0
- data/spec/grape/dsl/validations_spec.rb +2 -0
- data/spec/grape/endpoint/declared_spec.rb +601 -0
- data/spec/grape/endpoint_spec.rb +53 -523
- data/spec/grape/entity_spec.rb +9 -1
- data/spec/grape/exceptions/base_spec.rb +67 -0
- data/spec/grape/exceptions/body_parse_errors_spec.rb +2 -0
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +2 -0
- data/spec/grape/exceptions/invalid_formatter_spec.rb +2 -0
- data/spec/grape/exceptions/invalid_response_spec.rb +13 -0
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -0
- data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
- data/spec/grape/exceptions/missing_option_spec.rb +2 -0
- data/spec/grape/exceptions/unknown_options_spec.rb +2 -0
- data/spec/grape/exceptions/unknown_validator_spec.rb +2 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +8 -4
- data/spec/grape/exceptions/validation_spec.rb +3 -1
- data/spec/grape/extensions/param_builders/hash_spec.rb +2 -0
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +2 -0
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +2 -0
- data/spec/grape/integration/global_namespace_function_spec.rb +2 -0
- data/spec/grape/integration/rack_sendfile_spec.rb +14 -8
- data/spec/grape/integration/rack_spec.rb +25 -7
- data/spec/grape/loading_spec.rb +2 -0
- data/spec/grape/middleware/auth/base_spec.rb +2 -0
- data/spec/grape/middleware/auth/dsl_spec.rb +5 -3
- data/spec/grape/middleware/auth/strategies_spec.rb +3 -1
- data/spec/grape/middleware/base_spec.rb +10 -0
- data/spec/grape/middleware/error_spec.rb +3 -1
- data/spec/grape/middleware/exception_spec.rb +4 -2
- data/spec/grape/middleware/formatter_spec.rb +33 -16
- data/spec/grape/middleware/globals_spec.rb +2 -0
- data/spec/grape/middleware/stack_spec.rb +12 -0
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
- data/spec/grape/middleware/versioner/header_spec.rb +9 -1
- data/spec/grape/middleware/versioner/param_spec.rb +3 -1
- data/spec/grape/middleware/versioner/path_spec.rb +3 -1
- data/spec/grape/middleware/versioner_spec.rb +2 -0
- data/spec/grape/named_api_spec.rb +21 -0
- data/spec/grape/parser_spec.rb +7 -5
- data/spec/grape/path_spec.rb +6 -4
- data/spec/grape/presenters/presenter_spec.rb +2 -0
- data/spec/grape/request_spec.rb +26 -0
- data/spec/grape/util/inheritable_setting_spec.rb +2 -0
- data/spec/grape/util/inheritable_values_spec.rb +2 -0
- data/spec/grape/util/reverse_stackable_values_spec.rb +2 -0
- data/spec/grape/util/stackable_values_spec.rb +3 -1
- data/spec/grape/util/strict_hash_configuration_spec.rb +2 -0
- data/spec/grape/validations/attributes_iterator_spec.rb +2 -0
- data/spec/grape/validations/instance_behaivour_spec.rb +5 -3
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +41 -0
- data/spec/grape/validations/params_scope_spec.rb +213 -9
- data/spec/grape/validations/single_attribute_iterator_spec.rb +58 -0
- data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
- data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
- data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
- data/spec/grape/validations/types_spec.rb +9 -36
- data/spec/grape/validations/validators/all_or_none_spec.rb +140 -30
- data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +175 -29
- data/spec/grape/validations/validators/coerce_spec.rb +476 -135
- data/spec/grape/validations/validators/default_spec.rb +172 -0
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +204 -38
- data/spec/grape/validations/validators/except_values_spec.rb +4 -1
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +186 -27
- data/spec/grape/validations/validators/presence_spec.rb +30 -0
- data/spec/grape/validations/validators/regexp_spec.rb +2 -0
- data/spec/grape/validations/validators/same_as_spec.rb +65 -0
- data/spec/grape/validations/validators/values_spec.rb +30 -5
- data/spec/grape/validations_spec.rb +388 -50
- data/spec/integration/eager_load/eager_load_spec.rb +15 -0
- data/spec/integration/multi_json/json_spec.rb +2 -0
- data/spec/integration/multi_xml/xml_spec.rb +2 -0
- data/spec/shared/versioning_examples.rb +22 -20
- data/spec/spec_helper.rb +12 -1
- data/spec/support/basic_auth_encode_helpers.rb +2 -0
- data/spec/support/chunks.rb +14 -0
- data/spec/support/content_type_helpers.rb +2 -0
- data/spec/support/eager_load.rb +19 -0
- data/spec/support/endpoint_faker.rb +2 -0
- data/spec/support/file_streamer.rb +2 -0
- data/spec/support/integer_helpers.rb +2 -0
- data/spec/support/versioned_helpers.rb +8 -8
- metadata +86 -48
- 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/pkg/grape-0.17.0.gem +0 -0
- data/pkg/grape-0.19.0.gem +0 -0
@@ -0,0 +1,65 @@
|
|
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
|
+
register_collection Array
|
18
|
+
|
19
|
+
def initialize(type, strict = false)
|
20
|
+
super
|
21
|
+
|
22
|
+
@coercer = scope::Array
|
23
|
+
@subtype = type.first
|
24
|
+
end
|
25
|
+
|
26
|
+
def call(_val)
|
27
|
+
collection = super
|
28
|
+
return collection if collection.is_a?(InvalidValue)
|
29
|
+
|
30
|
+
coerce_elements collection
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
attr_reader :subtype
|
36
|
+
|
37
|
+
def coerce_elements(collection)
|
38
|
+
return if collection.nil?
|
39
|
+
|
40
|
+
collection.each_with_index do |elem, index|
|
41
|
+
return InvalidValue.new if reject?(elem)
|
42
|
+
|
43
|
+
coerced_elem = elem_coercer.call(elem)
|
44
|
+
|
45
|
+
return coerced_elem if coerced_elem.is_a?(InvalidValue)
|
46
|
+
|
47
|
+
collection[index] = coerced_elem
|
48
|
+
end
|
49
|
+
|
50
|
+
collection
|
51
|
+
end
|
52
|
+
|
53
|
+
# This method maintains logic which was defined by Virtus for arrays.
|
54
|
+
# Virtus doesn't allow nil in arrays.
|
55
|
+
def reject?(val)
|
56
|
+
val.nil?
|
57
|
+
end
|
58
|
+
|
59
|
+
def elem_coercer
|
60
|
+
@elem_coercer ||= DryTypeCoercer.coercer_instance_for(subtype, strict)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -1,78 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'array_coercer'
|
4
|
+
require_relative 'set_coercer'
|
5
|
+
require_relative 'primitive_coercer'
|
6
|
+
|
1
7
|
module Grape
|
2
8
|
module Validations
|
3
9
|
module Types
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
10
|
+
# Chooses the best coercer for the given type. For example, if the type
|
11
|
+
# is Integer, it will return a coercer which will be able to coerce a value
|
12
|
+
# to the integer.
|
13
|
+
#
|
14
|
+
# There are a few very special coercers which might be returned.
|
15
|
+
#
|
16
|
+
# +Grape::Types::MultipleTypeCoercer+ is a coercer which is returned when
|
17
|
+
# the given type implies values in an array with different types.
|
18
|
+
# For example, +[Integer, String]+ allows integer and string values in
|
19
|
+
# an array.
|
20
|
+
#
|
21
|
+
# +Grape::Types::CustomTypeCoercer+ is a coercer which is returned when
|
22
|
+
# a method is specified by a user with +coerce_with+ option or the user
|
23
|
+
# specifies a custom type which implements requirments of
|
24
|
+
# +Grape::Types::CustomTypeCoercer+.
|
8
25
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# will be ignored.
|
26
|
+
# +Grape::Types::CustomTypeCollectionCoercer+ is a very similar to the
|
27
|
+
# previous one, but it expects an array or set of values having a custom
|
28
|
+
# type implemented by the user.
|
13
29
|
#
|
14
|
-
#
|
15
|
-
#
|
30
|
+
# There is also a group of custom types implemented by Grape, check
|
31
|
+
# +Grape::Validations::Types::SPECIAL+ to get the full list.
|
16
32
|
#
|
17
33
|
# @param type [Class] the type to which input strings
|
18
34
|
# should be coerced
|
19
35
|
# @param method [Class,#call] the coercion method to use
|
20
|
-
# @return [
|
36
|
+
# @return [Object] object to be used
|
21
37
|
# for coercion and type validation
|
22
|
-
def self.build_coercer(type, method
|
23
|
-
cache_instance(type, method) do
|
24
|
-
create_coercer_instance(type, method)
|
38
|
+
def self.build_coercer(type, method: nil, strict: false)
|
39
|
+
cache_instance(type, method, strict) do
|
40
|
+
create_coercer_instance(type, method, strict)
|
25
41
|
end
|
26
42
|
end
|
27
43
|
|
28
|
-
def self.create_coercer_instance(type, method
|
29
|
-
#
|
30
|
-
|
31
|
-
|
32
|
-
converter_options = {
|
33
|
-
nullify_blank: true
|
34
|
-
}
|
35
|
-
conversion_type = if method == JSON
|
36
|
-
Object
|
37
|
-
# because we want just parsed JSON content:
|
38
|
-
# if type is Array and data is `"{}"`
|
39
|
-
# result will be [] because Virtus converts hashes
|
40
|
-
# to arrays
|
41
|
-
else
|
42
|
-
type
|
43
|
-
end
|
44
|
+
def self.create_coercer_instance(type, method, strict)
|
45
|
+
# Maps a custom type provided by Grape, it doesn't map types wrapped by collections!!!
|
46
|
+
type = Types.map_special(type)
|
44
47
|
|
45
48
|
# Use a special coercer for multiply-typed parameters.
|
46
49
|
if Types.multiple?(type)
|
47
|
-
|
48
|
-
conversion_type = Object
|
50
|
+
MultipleTypeCoercer.new(type, method)
|
49
51
|
|
50
52
|
# Use a special coercer for custom types and coercion methods.
|
51
53
|
elsif method || Types.custom?(type)
|
52
|
-
|
54
|
+
CustomTypeCoercer.new(type, method)
|
53
55
|
|
54
56
|
# Special coercer for collections of types that implement a parse method.
|
55
57
|
# CustomTypeCoercer (above) already handles such types when an explicit coercion
|
56
58
|
# method is supplied.
|
57
59
|
elsif Types.collection_of_custom?(type)
|
58
|
-
|
59
|
-
type.first, type.is_a?(Set)
|
60
|
+
Types::CustomTypeCollectionCoercer.new(
|
61
|
+
Types.map_special(type.first), type.is_a?(Set)
|
60
62
|
)
|
61
|
-
|
62
|
-
|
63
|
-
# for certain special types that merit first-class support
|
64
|
-
# (but not if a custom coercion method has been supplied).
|
65
|
-
elsif Types.special?(type)
|
66
|
-
conversion_type = Types::SPECIAL[type]
|
63
|
+
else
|
64
|
+
DryTypeCoercer.coercer_instance_for(type, strict)
|
67
65
|
end
|
68
|
-
|
69
|
-
# Virtus will infer coercion and validation rules
|
70
|
-
# for many common ruby types.
|
71
|
-
Virtus::Attribute.build(conversion_type, converter_options)
|
72
66
|
end
|
73
67
|
|
74
|
-
def self.cache_instance(type, method, &_block)
|
75
|
-
key = cache_key(type, method)
|
68
|
+
def self.cache_instance(type, method, strict, &_block)
|
69
|
+
key = cache_key(type, method, strict)
|
76
70
|
|
77
71
|
return @__cache[key] if @__cache.key?(key)
|
78
72
|
|
@@ -85,8 +79,12 @@ module Grape
|
|
85
79
|
instance
|
86
80
|
end
|
87
81
|
|
88
|
-
def self.cache_key(type, method)
|
89
|
-
[type, method].
|
82
|
+
def self.cache_key(type, method, strict)
|
83
|
+
[type, method, strict].each_with_object(+'_') do |val, memo|
|
84
|
+
next if val.nil?
|
85
|
+
|
86
|
+
memo << '_' << val.to_s
|
87
|
+
end
|
90
88
|
end
|
91
89
|
|
92
90
|
instance_variable_set(:@__cache, {})
|
@@ -1,21 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Validations
|
3
5
|
module Types
|
4
|
-
# Instances of this class may be passed to
|
5
|
-
# +Virtus::Attribute.build+ as the +:coercer+
|
6
|
-
# option for custom types that do not otherwise
|
7
|
-
# satisfy the requirements for +Virtus::Attribute::coerce+
|
8
|
-
# and +Virtus::Attribute::value_coerced?+ to work
|
9
|
-
# as expected.
|
10
|
-
#
|
11
|
-
# Subclasses of +Virtus::Attribute+ or +Axiom::Types::Type+
|
12
|
-
# (or for which an axiom type can be inferred, i.e. the
|
13
|
-
# primitives, +Date+, +Time+, etc.) do not need any such
|
14
|
-
# coercer to be passed with them.
|
15
|
-
#
|
16
|
-
# Coercion
|
17
|
-
# --------
|
18
|
-
#
|
19
6
|
# This class will detect type classes that implement
|
20
7
|
# a class-level +parse+ method. The method should accept one
|
21
8
|
# +String+ argument and should return the value coerced to
|
@@ -30,14 +17,14 @@ module Grape
|
|
30
17
|
# Type Checking
|
31
18
|
# -------------
|
32
19
|
#
|
33
|
-
# Calls to +
|
20
|
+
# Calls to +coerced?+ will consult this class to check
|
34
21
|
# that the coerced value produced above is in fact of the
|
35
22
|
# expected type. By default this class performs a basic check
|
36
23
|
# against the type supplied, but this behaviour will be
|
37
24
|
# overridden if the class implements a class-level
|
38
25
|
# +coerced?+ or +parsed?+ method. This method
|
39
26
|
# will receive a single parameter that is the coerced value
|
40
|
-
# and should return +true+
|
27
|
+
# and should return +true+ if the value meets type expectations.
|
41
28
|
# Arbitrary assertions may be made here but the grape validation
|
42
29
|
# system should be preferred.
|
43
30
|
#
|
@@ -46,15 +33,6 @@ module Grape
|
|
46
33
|
# contract as +coerced?+, and must be supplied with a coercion
|
47
34
|
# +method+.
|
48
35
|
class CustomTypeCoercer
|
49
|
-
# Uses +Virtus::Attribute.build+ to build a new
|
50
|
-
# attribute that makes use of this class for
|
51
|
-
# coercion and type validation logic.
|
52
|
-
#
|
53
|
-
# @return [Virtus::Attribute]
|
54
|
-
def self.build(type, method = nil)
|
55
|
-
Virtus::Attribute.build(type, coercer: new(type, method))
|
56
|
-
end
|
57
|
-
|
58
36
|
# A new coercer for the given type specification
|
59
37
|
# and coercion method.
|
60
38
|
#
|
@@ -64,37 +42,25 @@ module Grape
|
|
64
42
|
# optional coercion method. See class docs.
|
65
43
|
def initialize(type, method = nil)
|
66
44
|
coercion_method = infer_coercion_method type, method
|
67
|
-
|
68
45
|
@method = enforce_symbolized_keys type, coercion_method
|
69
|
-
|
70
46
|
@type_check = infer_type_check(type)
|
71
47
|
end
|
72
48
|
|
73
|
-
#
|
74
|
-
# +Virtus::Attribute::coerce+ in order to coerce
|
75
|
-
# the given value.
|
49
|
+
# Coerces the given value.
|
76
50
|
#
|
77
51
|
# @param value [String] value to be coerced, in grape
|
78
52
|
# this should always be a string.
|
79
53
|
# @return [Object] the coerced result
|
80
|
-
def call(
|
81
|
-
@method.call
|
54
|
+
def call(val)
|
55
|
+
coerced_val = @method.call(val)
|
56
|
+
|
57
|
+
return coerced_val if coerced_val.is_a?(InvalidValue)
|
58
|
+
return InvalidValue.new unless coerced?(coerced_val)
|
59
|
+
coerced_val
|
82
60
|
end
|
83
61
|
|
84
|
-
|
85
|
-
|
86
|
-
# assert that the value has been coerced successfully.
|
87
|
-
#
|
88
|
-
# @param _primitive [Axiom::Types::Type] primitive type
|
89
|
-
# for the coercion as detected by axiom-types' inference
|
90
|
-
# system. For custom types this is typically not much use
|
91
|
-
# (i.e. it is +Axiom::Types::Object+) unless special
|
92
|
-
# inference rules have been declared for the type.
|
93
|
-
# @param value [Object] a coerced result returned from {#call}
|
94
|
-
# @return [true,false] whether or not the coerced value
|
95
|
-
# satisfies type requirements.
|
96
|
-
def success?(_primitive, value)
|
97
|
-
@type_check.call value
|
62
|
+
def coerced?(val)
|
63
|
+
val.nil? || @type_check.call(val)
|
98
64
|
end
|
99
65
|
|
100
66
|
private
|
@@ -137,13 +103,25 @@ module Grape
|
|
137
103
|
# passed, or if the type also implements a parse() method.
|
138
104
|
type
|
139
105
|
elsif type.is_a?(Enumerable)
|
140
|
-
|
106
|
+
lambda do |value|
|
107
|
+
value.is_a?(Enumerable) && value.all? do |val|
|
108
|
+
recursive_type_check(type.first, val)
|
109
|
+
end
|
110
|
+
end
|
141
111
|
else
|
142
112
|
# By default, do a simple type check
|
143
113
|
->(value) { value.is_a? type }
|
144
114
|
end
|
145
115
|
end
|
146
116
|
|
117
|
+
def recursive_type_check(type, value)
|
118
|
+
if type.is_a?(Enumerable) && value.is_a?(Enumerable)
|
119
|
+
value.all? { |val| recursive_type_check(type.first, val) }
|
120
|
+
else
|
121
|
+
!type.is_a?(Enumerable) && value.is_a?(type)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
147
125
|
# Enforce symbolized keys for complex types
|
148
126
|
# by wrapping the coercion method such that
|
149
127
|
# any Hash objects in the immediate heirarchy
|
@@ -158,10 +136,10 @@ module Grape
|
|
158
136
|
# necessary.
|
159
137
|
def enforce_symbolized_keys(type, method)
|
160
138
|
# Collections have all values processed individually
|
161
|
-
if
|
139
|
+
if [Array, Set].include?(type)
|
162
140
|
lambda do |val|
|
163
|
-
method.call(val).tap do |
|
164
|
-
|
141
|
+
method.call(val).tap do |new_val|
|
142
|
+
new_val.map do |item|
|
165
143
|
item.is_a?(Hash) ? symbolize_keys(item) : item
|
166
144
|
end
|
167
145
|
end
|
@@ -1,12 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Validations
|
3
5
|
module Types
|
4
|
-
# Instances of this class may be passed to
|
5
|
-
# +Virtus::Attribute.build+ as the +:coercer+
|
6
|
-
# option, to handle collections of types that
|
7
|
-
# provide their own parsing (and optionally,
|
8
|
-
# type-checking) functionality.
|
9
|
-
#
|
10
6
|
# See {CustomTypeCoercer} for details on types
|
11
7
|
# that will be supported by this by this coercer.
|
12
8
|
# This coercer works in the same way as +CustomTypeCoercer+
|
@@ -38,32 +34,21 @@ module Grape
|
|
38
34
|
@set = set
|
39
35
|
end
|
40
36
|
|
41
|
-
#
|
42
|
-
# +Virtus::Attribute::coerce+ in order to coerce
|
43
|
-
# the given value.
|
37
|
+
# Coerces the given value.
|
44
38
|
#
|
45
39
|
# @param value [Array<String>] an array of values to be coerced
|
46
40
|
# @return [Array,Set] the coerced result. May be an +Array+ or a
|
47
41
|
# +Set+ depending on the setting given to the constructor
|
48
42
|
def call(value)
|
49
|
-
coerced = value.map
|
43
|
+
coerced = value.map do |item|
|
44
|
+
coerced_item = super(item)
|
50
45
|
|
51
|
-
|
52
|
-
end
|
46
|
+
return coerced_item if coerced_item.is_a?(InvalidValue)
|
53
47
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
#
|
59
|
-
# @param primitive [Axiom::Types::Type] primitive type for
|
60
|
-
# the coercion as deteced by axiom-types' inference system.
|
61
|
-
# @param value [Enumerable] a coerced result returned from {#call}
|
62
|
-
# @return [true,false] whether or not all of the coerced values in
|
63
|
-
# the collection satisfy type requirements.
|
64
|
-
def success?(primitive, value)
|
65
|
-
value.is_a?(@set ? Set : Array) &&
|
66
|
-
value.all? { |item| super(primitive, item) }
|
48
|
+
coerced_item
|
49
|
+
end
|
50
|
+
|
51
|
+
@set ? Set.new(coerced) : coerced
|
67
52
|
end
|
68
53
|
end
|
69
54
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry-types'
|
4
|
+
|
5
|
+
module DryTypes
|
6
|
+
# Call +Dry.Types()+ to add all registered types to +DryTypes+ which is
|
7
|
+
# a container in this case. Check documentation for more information
|
8
|
+
# https://dry-rb.org/gems/dry-types/1.2/getting-started/
|
9
|
+
include Dry.Types()
|
10
|
+
end
|
11
|
+
|
12
|
+
module Grape
|
13
|
+
module Validations
|
14
|
+
module Types
|
15
|
+
# A base class for classes which must identify a coercer to be used.
|
16
|
+
# If the +strict+ argument is true, it won't coerce the given value
|
17
|
+
# but check its type. More information there
|
18
|
+
# https://dry-rb.org/gems/dry-types/1.2/built-in-types/
|
19
|
+
class DryTypeCoercer
|
20
|
+
class << self
|
21
|
+
# Registers a collection coercer which could be found by a type,
|
22
|
+
# see +collection_coercer_for+ method below. This method is meant for inheritors.
|
23
|
+
def register_collection(type)
|
24
|
+
DryTypeCoercer.collection_coercers[type] = self
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns a collection coercer which corresponds to a given type.
|
28
|
+
# Example:
|
29
|
+
#
|
30
|
+
# collection_coercer_for(Array)
|
31
|
+
# #=> Grape::Validations::Types::ArrayCoercer
|
32
|
+
def collection_coercer_for(type)
|
33
|
+
collection_coercers[type]
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns an instance of a coercer for a given type
|
37
|
+
def coercer_instance_for(type, strict = false)
|
38
|
+
return PrimitiveCoercer.new(type, strict) if type.class == Class
|
39
|
+
|
40
|
+
# in case of a collection (Array[Integer]) the type is an instance of a collection,
|
41
|
+
# so we need to figure out the actual type
|
42
|
+
collection_coercer_for(type.class).new(type, strict)
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
def collection_coercers
|
48
|
+
@collection_coercers ||= {}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize(type, strict = false)
|
53
|
+
@type = type
|
54
|
+
@strict = strict
|
55
|
+
@scope = strict ? DryTypes::Strict : DryTypes::Params
|
56
|
+
end
|
57
|
+
|
58
|
+
# Coerces the given value to a type which was specified during
|
59
|
+
# initialization as a type argument.
|
60
|
+
#
|
61
|
+
# @param val [Object]
|
62
|
+
def call(val)
|
63
|
+
return if val.nil?
|
64
|
+
|
65
|
+
@coercer[val]
|
66
|
+
rescue Dry::Types::CoercionError => _e
|
67
|
+
InvalidValue.new
|
68
|
+
end
|
69
|
+
|
70
|
+
protected
|
71
|
+
|
72
|
+
attr_reader :scope, :type, :strict
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|