grape 1.2.5 → 1.4.0
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 +97 -0
- data/LICENSE +1 -1
- data/README.md +53 -16
- data/UPGRADING.md +231 -23
- data/grape.gemspec +10 -1
- data/lib/grape.rb +6 -7
- data/lib/grape/api.rb +4 -2
- data/lib/grape/api/helpers.rb +2 -0
- data/lib/grape/api/instance.rb +36 -33
- data/lib/grape/config.rb +2 -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 +2 -0
- data/lib/grape/dsl/configuration.rb +2 -0
- data/lib/grape/dsl/desc.rb +2 -0
- data/lib/grape/dsl/headers.rb +2 -0
- data/lib/grape/dsl/helpers.rb +4 -2
- data/lib/grape/dsl/inside_route.rb +83 -34
- data/lib/grape/dsl/logger.rb +2 -0
- data/lib/grape/dsl/middleware.rb +2 -0
- data/lib/grape/dsl/parameters.rb +8 -6
- data/lib/grape/dsl/request_response.rb +4 -2
- data/lib/grape/dsl/routing.rb +9 -5
- data/lib/grape/dsl/settings.rb +7 -1
- data/lib/grape/dsl/validations.rb +20 -1
- data/lib/grape/eager_load.rb +3 -1
- data/lib/grape/endpoint.rb +21 -13
- data/lib/grape/error_formatter.rb +3 -1
- 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 +11 -13
- 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 +2 -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 +3 -1
- data/lib/grape/exceptions/validation_array_errors.rb +2 -0
- data/lib/grape/exceptions/validation_errors.rb +13 -12
- 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/middleware/auth/base.rb +2 -0
- 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 +7 -7
- data/lib/grape/middleware/error.rb +3 -1
- 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 +2 -0
- data/lib/grape/middleware/stack.rb +4 -1
- 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 +6 -4
- 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 +3 -1
- data/lib/grape/parser/json.rb +2 -0
- data/lib/grape/parser/xml.rb +2 -0
- data/lib/grape/path.rb +15 -3
- data/lib/grape/presenters/presenter.rb +2 -0
- data/lib/grape/request.rb +15 -8
- data/lib/grape/router.rb +30 -29
- data/lib/grape/router/attribute_translator.rb +39 -8
- data/lib/grape/router/pattern.rb +20 -16
- data/lib/grape/router/route.rb +12 -26
- 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 +15 -6
- data/lib/grape/util/cache.rb +20 -0
- data/lib/grape/util/endpoint_configuration.rb +2 -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 +2 -0
- data/lib/grape/util/json.rb +2 -0
- data/lib/grape/util/lazy_block.rb +2 -0
- data/lib/grape/util/lazy_object.rb +43 -0
- data/lib/grape/util/lazy_value.rb +2 -0
- data/lib/grape/util/registrable.rb +2 -0
- data/lib/grape/util/reverse_stackable_values.rb +4 -0
- data/lib/grape/util/stackable_values.rb +10 -20
- 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 +3 -3
- data/lib/grape/validations/multiple_attributes_iterator.rb +2 -0
- data/lib/grape/validations/params_scope.rb +27 -14
- data/lib/grape/validations/single_attribute_iterator.rb +13 -2
- data/lib/grape/validations/types.rb +12 -34
- 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 +15 -49
- 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/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 +2 -0
- data/lib/grape/validations/validators/all_or_none.rb +3 -1
- data/lib/grape/validations/validators/allow_blank.rb +3 -1
- data/lib/grape/validations/validators/as.rb +2 -0
- data/lib/grape/validations/validators/at_least_one_of.rb +3 -1
- data/lib/grape/validations/validators/base.rb +8 -5
- data/lib/grape/validations/validators/coerce.rb +39 -29
- data/lib/grape/validations/validators/default.rb +2 -1
- data/lib/grape/validations/validators/exactly_one_of.rb +6 -2
- data/lib/grape/validations/validators/except_values.rb +3 -1
- data/lib/grape/validations/validators/multiple_params_base.rb +2 -0
- data/lib/grape/validations/validators/mutual_exclusion.rb +3 -1
- 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 +6 -3
- data/lib/grape/validations/validators/values.rb +17 -5
- 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 +5 -3
- 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 +2 -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 +2 -0
- data/spec/grape/api_spec.rb +99 -11
- data/spec/grape/config_spec.rb +2 -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 +2 -0
- 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 +177 -33
- data/spec/grape/dsl/logger_spec.rb +2 -0
- data/spec/grape/dsl/middleware_spec.rb +2 -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 +2 -0
- data/spec/grape/dsl/settings_spec.rb +2 -0
- data/spec/grape/dsl/validations_spec.rb +2 -0
- data/spec/grape/endpoint_spec.rb +21 -6
- data/spec/grape/entity_spec.rb +2 -0
- data/spec/grape/exceptions/base_spec.rb +3 -1
- 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 +2 -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 +4 -2
- 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 +3 -1
- 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 +2 -0
- data/spec/grape/middleware/auth/strategies_spec.rb +3 -1
- data/spec/grape/middleware/base_spec.rb +2 -0
- data/spec/grape/middleware/error_spec.rb +2 -0
- data/spec/grape/middleware/exception_spec.rb +3 -1
- data/spec/grape/middleware/formatter_spec.rb +19 -12
- data/spec/grape/middleware/globals_spec.rb +2 -0
- data/spec/grape/middleware/stack_spec.rb +11 -0
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
- data/spec/grape/middleware/versioner/header_spec.rb +3 -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 +2 -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 +2 -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 +2 -0
- data/spec/grape/validations/params_scope_spec.rb +3 -1
- data/spec/grape/validations/single_attribute_iterator_spec.rb +18 -4
- 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 +2 -0
- data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +2 -0
- data/spec/grape/validations/validators/coerce_spec.rb +341 -136
- data/spec/grape/validations/validators/default_spec.rb +123 -0
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +14 -12
- data/spec/grape/validations/validators/except_values_spec.rb +3 -1
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +2 -0
- 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 +2 -0
- data/spec/grape/validations/validators/values_spec.rb +30 -5
- data/spec/grape/validations_spec.rb +91 -33
- 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 +2 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/basic_auth_encode_helpers.rb +2 -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 +4 -2
- metadata +48 -28
- 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/util/xml.rb
CHANGED
data/lib/grape/validations.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Validations
|
3
5
|
class AttributesIterator
|
@@ -29,9 +31,7 @@ module Grape
|
|
29
31
|
|
30
32
|
if @scope.type == Array
|
31
33
|
next unless @original_params.is_a?(Array) # do not validate content of array if it isn't array
|
32
|
-
|
33
|
-
end
|
34
|
-
if inside_array
|
34
|
+
|
35
35
|
# fill current and parent scopes with correct array indicies
|
36
36
|
parent_scope = @scope.parent
|
37
37
|
parent_indicies.each do |parent_index|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Validations
|
3
5
|
class ParamsScope
|
@@ -43,8 +45,10 @@ module Grape
|
|
43
45
|
# @return [Boolean] whether or not this entire scope needs to be
|
44
46
|
# validated
|
45
47
|
def should_validate?(parameters)
|
46
|
-
|
47
|
-
|
48
|
+
scoped_params = params(parameters)
|
49
|
+
|
50
|
+
return false if @optional && (scoped_params.blank? || all_element_blank?(scoped_params))
|
51
|
+
return false unless meets_dependency?(scoped_params, parameters)
|
48
52
|
return true if parent.nil?
|
49
53
|
parent.should_validate?(parameters)
|
50
54
|
end
|
@@ -121,7 +125,7 @@ module Grape
|
|
121
125
|
# @param attrs [Array] (see Grape::DSL::Parameters#requires)
|
122
126
|
def push_declared_params(attrs, **opts)
|
123
127
|
if lateral?
|
124
|
-
@parent.push_declared_params(attrs, opts)
|
128
|
+
@parent.push_declared_params(attrs, **opts)
|
125
129
|
else
|
126
130
|
if opts && opts[:as]
|
127
131
|
@api.route_setting(:renamed_params, @api.route_setting(:renamed_params) || [])
|
@@ -233,14 +237,14 @@ module Grape
|
|
233
237
|
@parent.push_declared_params [element => @declared_params]
|
234
238
|
else
|
235
239
|
@api.namespace_stackable(:declared_params, @declared_params)
|
236
|
-
|
237
|
-
@api.route_setting(:declared_params, []) unless @api.route_setting(:declared_params)
|
238
|
-
@api.route_setting(:declared_params, @api.namespace_stackable(:declared_params).flatten)
|
239
240
|
end
|
241
|
+
|
242
|
+
# params were stored in settings, it can be cleaned from the params scope
|
243
|
+
@declared_params = nil
|
240
244
|
end
|
241
245
|
|
242
246
|
def validates(attrs, validations)
|
243
|
-
doc_attrs = { required: validations.
|
247
|
+
doc_attrs = { required: validations.key?(:presence) }
|
244
248
|
|
245
249
|
coerce_type = infer_coercion(validations)
|
246
250
|
|
@@ -280,9 +284,7 @@ module Grape
|
|
280
284
|
full_attrs = attrs.collect { |name| { name: name, full_name: full_name(name) } }
|
281
285
|
@api.document_attribute(full_attrs, doc_attrs)
|
282
286
|
|
283
|
-
|
284
|
-
opts = {}
|
285
|
-
opts[:fail_fast] = validations.delete(:fail_fast) || false
|
287
|
+
opts = derive_validator_options(validations)
|
286
288
|
|
287
289
|
# Validate for presence before any other validators
|
288
290
|
if validations.key?(:presence) && validations[:presence]
|
@@ -427,8 +429,8 @@ module Grape
|
|
427
429
|
values_list.each do |values|
|
428
430
|
next if !values || values.is_a?(Proc)
|
429
431
|
value_types = values.is_a?(Range) ? [values.begin, values.end] : values
|
430
|
-
if coerce_type ==
|
431
|
-
value_types = value_types.map { |type|
|
432
|
+
if coerce_type == Grape::API::Boolean
|
433
|
+
value_types = value_types.map { |type| Grape::API::Boolean.build(type) }
|
432
434
|
end
|
433
435
|
unless value_types.all? { |v| v.is_a? coerce_type }
|
434
436
|
raise Grape::Exceptions::IncompatibleOptionValues.new(:type, coerce_type, :values, values)
|
@@ -446,8 +448,19 @@ module Grape
|
|
446
448
|
validations[type].respond_to?(:key?) && validations[type].key?(key) && !validations[type][key].nil?
|
447
449
|
end
|
448
450
|
|
449
|
-
def all_element_blank?(
|
450
|
-
|
451
|
+
def all_element_blank?(scoped_params)
|
452
|
+
scoped_params.respond_to?(:all?) && scoped_params.all?(&:blank?)
|
453
|
+
end
|
454
|
+
|
455
|
+
# Validators don't have access to each other and they don't need, however,
|
456
|
+
# some validators might influence others, so their options should be shared
|
457
|
+
def derive_validator_options(validations)
|
458
|
+
allow_blank = validations[:allow_blank]
|
459
|
+
|
460
|
+
{
|
461
|
+
allow_blank: allow_blank.is_a?(Hash) ? allow_blank[:value] : allow_blank,
|
462
|
+
fail_fast: validations.delete(:fail_fast) || false
|
463
|
+
}
|
451
464
|
end
|
452
465
|
end
|
453
466
|
end
|
@@ -1,13 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Validations
|
3
5
|
class SingleAttributeIterator < AttributesIterator
|
4
6
|
private
|
5
7
|
|
6
|
-
def yield_attributes(
|
8
|
+
def yield_attributes(val, attrs)
|
7
9
|
attrs.each do |attr_name|
|
8
|
-
yield
|
10
|
+
yield val, attr_name, empty?(val)
|
9
11
|
end
|
10
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
|
11
22
|
end
|
12
23
|
end
|
13
24
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'types/build_coercer'
|
2
4
|
require_relative 'types/custom_type_coercer'
|
3
5
|
require_relative 'types/custom_type_collection_coercer'
|
@@ -6,10 +8,6 @@ require_relative 'types/variant_collection_coercer'
|
|
6
8
|
require_relative 'types/json'
|
7
9
|
require_relative 'types/file'
|
8
10
|
|
9
|
-
# Patch for Virtus::Attribute::Collection
|
10
|
-
# See the file for more details
|
11
|
-
require_relative 'types/virtus_collection_patch'
|
12
|
-
|
13
11
|
module Grape
|
14
12
|
module Validations
|
15
13
|
# Module for code related to grape's system for
|
@@ -27,8 +25,7 @@ module Grape
|
|
27
25
|
# a parameter value could not be coerced.
|
28
26
|
class InvalidValue; end
|
29
27
|
|
30
|
-
# Types representing a single value, which are coerced
|
31
|
-
# or special logic in Grape.
|
28
|
+
# Types representing a single value, which are coerced.
|
32
29
|
PRIMITIVES = [
|
33
30
|
# Numerical
|
34
31
|
Integer,
|
@@ -42,10 +39,11 @@ module Grape
|
|
42
39
|
Time,
|
43
40
|
|
44
41
|
# Misc
|
45
|
-
|
42
|
+
Grape::API::Boolean,
|
46
43
|
String,
|
47
44
|
Symbol,
|
48
|
-
|
45
|
+
TrueClass,
|
46
|
+
FalseClass
|
49
47
|
].freeze
|
50
48
|
|
51
49
|
# Types representing data structures.
|
@@ -55,8 +53,7 @@ module Grape
|
|
55
53
|
Set
|
56
54
|
].freeze
|
57
55
|
|
58
|
-
#
|
59
|
-
# and type-checking logic.
|
56
|
+
# Special custom types provided by Grape.
|
60
57
|
SPECIAL = {
|
61
58
|
JSON => Json,
|
62
59
|
Array[JSON] => JsonArray,
|
@@ -86,8 +83,6 @@ module Grape
|
|
86
83
|
# @param type [Class] type to check
|
87
84
|
# @return [Boolean] whether or not the type is known by Grape as a valid
|
88
85
|
# data structure type
|
89
|
-
# @note This method does not yet consider 'complex types', which inherit
|
90
|
-
# Virtus.model.
|
91
86
|
def self.structure?(type)
|
92
87
|
STRUCTURES.include?(type)
|
93
88
|
end
|
@@ -104,25 +99,6 @@ module Grape
|
|
104
99
|
(type.is_a?(Array) || type.is_a?(Set)) && type.size > 1
|
105
100
|
end
|
106
101
|
|
107
|
-
# Does the given class implement a type system that Grape
|
108
|
-
# (i.e. the underlying virtus attribute system) supports
|
109
|
-
# out-of-the-box? Currently supported are +axiom-types+
|
110
|
-
# and +virtus+.
|
111
|
-
#
|
112
|
-
# The type will be passed to +Virtus::Attribute.build+,
|
113
|
-
# and the resulting attribute object will be expected to
|
114
|
-
# respond correctly to +coerce+ and +value_coerced?+.
|
115
|
-
#
|
116
|
-
# @param type [Class] type to check
|
117
|
-
# @return [Boolean] +true+ where the type is recognized
|
118
|
-
def self.recognized?(type)
|
119
|
-
return false if type.is_a?(Array) || type.is_a?(Set)
|
120
|
-
|
121
|
-
type.is_a?(Virtus::Attribute) ||
|
122
|
-
type.ancestors.include?(Axiom::Types::Type) ||
|
123
|
-
type.include?(Virtus::Model::Core)
|
124
|
-
end
|
125
|
-
|
126
102
|
# Does Grape provide special coercion and validation
|
127
103
|
# routines for the given class? This does not include
|
128
104
|
# automatic handling for primitives, structures and
|
@@ -152,8 +128,6 @@ module Grape
|
|
152
128
|
!primitive?(type) &&
|
153
129
|
!structure?(type) &&
|
154
130
|
!multiple?(type) &&
|
155
|
-
!recognized?(type) &&
|
156
|
-
!special?(type) &&
|
157
131
|
type.respond_to?(:parse) &&
|
158
132
|
type.method(:parse).arity == 1
|
159
133
|
end
|
@@ -166,7 +140,11 @@ module Grape
|
|
166
140
|
def self.collection_of_custom?(type)
|
167
141
|
(type.is_a?(Array) || type.is_a?(Set)) &&
|
168
142
|
type.length == 1 &&
|
169
|
-
custom?(type.first)
|
143
|
+
(custom?(type.first) || special?(type.first))
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.map_special(type)
|
147
|
+
SPECIAL.fetch(type, type)
|
170
148
|
end
|
171
149
|
end
|
172
150
|
end
|
@@ -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, {})
|