grape 2.0.0 → 2.1.2
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 +69 -1
- data/README.md +362 -316
- data/UPGRADING.md +197 -7
- data/grape.gemspec +5 -6
- data/lib/grape/api/instance.rb +14 -11
- data/lib/grape/api.rb +19 -10
- data/lib/grape/content_types.rb +0 -2
- data/lib/grape/cookies.rb +2 -1
- data/lib/grape/dry_types.rb +0 -2
- data/lib/grape/dsl/desc.rb +22 -20
- data/lib/grape/dsl/headers.rb +1 -1
- data/lib/grape/dsl/inside_route.rb +42 -13
- data/lib/grape/dsl/parameters.rb +5 -4
- data/lib/grape/dsl/routing.rb +20 -4
- data/lib/grape/dsl/validations.rb +13 -0
- data/lib/grape/endpoint.rb +14 -17
- data/lib/grape/{util/env.rb → env.rb} +0 -5
- data/lib/grape/error_formatter/txt.rb +11 -10
- data/lib/grape/exceptions/base.rb +3 -3
- data/lib/grape/exceptions/validation.rb +0 -2
- data/lib/grape/exceptions/validation_array_errors.rb +1 -0
- data/lib/grape/exceptions/validation_errors.rb +2 -4
- data/lib/grape/extensions/hash.rb +5 -1
- data/lib/grape/http/headers.rb +18 -34
- data/lib/grape/{util/json.rb → json.rb} +1 -3
- data/lib/grape/locale/en.yml +3 -0
- data/lib/grape/middleware/auth/base.rb +0 -2
- data/lib/grape/middleware/auth/dsl.rb +0 -2
- data/lib/grape/middleware/base.rb +1 -3
- data/lib/grape/middleware/error.rb +55 -50
- data/lib/grape/middleware/formatter.rb +16 -13
- data/lib/grape/middleware/globals.rb +1 -3
- data/lib/grape/middleware/stack.rb +4 -5
- data/lib/grape/middleware/versioner/accept_version_header.rb +0 -2
- data/lib/grape/middleware/versioner/header.rb +17 -163
- data/lib/grape/middleware/versioner/param.rb +2 -4
- data/lib/grape/middleware/versioner/path.rb +1 -3
- data/lib/grape/namespace.rb +3 -4
- data/lib/grape/path.rb +24 -29
- data/lib/grape/request.rb +4 -12
- data/lib/grape/router/base_route.rb +39 -0
- data/lib/grape/router/greedy_route.rb +20 -0
- data/lib/grape/router/pattern.rb +39 -30
- data/lib/grape/router/route.rb +22 -59
- data/lib/grape/router.rb +32 -37
- data/lib/grape/util/accept_header_handler.rb +105 -0
- data/lib/grape/util/base_inheritable.rb +4 -4
- data/lib/grape/util/cache.rb +0 -3
- data/lib/grape/util/endpoint_configuration.rb +1 -1
- data/lib/grape/util/header.rb +13 -0
- data/lib/grape/util/inheritable_values.rb +0 -2
- data/lib/grape/util/lazy/block.rb +29 -0
- data/lib/grape/util/lazy/object.rb +45 -0
- data/lib/grape/util/lazy/value.rb +38 -0
- data/lib/grape/util/lazy/value_array.rb +21 -0
- data/lib/grape/util/lazy/value_enumerable.rb +34 -0
- data/lib/grape/util/lazy/value_hash.rb +21 -0
- data/lib/grape/util/media_type.rb +70 -0
- data/lib/grape/util/reverse_stackable_values.rb +1 -6
- data/lib/grape/util/stackable_values.rb +1 -6
- data/lib/grape/util/strict_hash_configuration.rb +3 -3
- data/lib/grape/validations/attributes_doc.rb +38 -36
- data/lib/grape/validations/attributes_iterator.rb +1 -0
- data/lib/grape/validations/contract_scope.rb +71 -0
- data/lib/grape/validations/params_scope.rb +15 -18
- data/lib/grape/validations/types/array_coercer.rb +0 -2
- data/lib/grape/validations/types/build_coercer.rb +69 -71
- data/lib/grape/validations/types/dry_type_coercer.rb +1 -11
- data/lib/grape/validations/types/json.rb +0 -2
- data/lib/grape/validations/types/primitive_coercer.rb +0 -2
- data/lib/grape/validations/types/set_coercer.rb +0 -3
- data/lib/grape/validations/types.rb +0 -3
- data/lib/grape/validations/validators/base.rb +1 -0
- data/lib/grape/validations/validators/default_validator.rb +5 -1
- data/lib/grape/validations/validators/exactly_one_of_validator.rb +1 -1
- data/lib/grape/validations/validators/length_validator.rb +42 -0
- data/lib/grape/validations/validators/values_validator.rb +6 -1
- data/lib/grape/validations.rb +3 -7
- data/lib/grape/version.rb +1 -1
- data/lib/grape/{util/xml.rb → xml.rb} +1 -1
- data/lib/grape.rb +30 -274
- metadata +30 -37
- data/lib/grape/eager_load.rb +0 -20
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +0 -24
- data/lib/grape/router/attribute_translator.rb +0 -63
- data/lib/grape/util/lazy_block.rb +0 -27
- data/lib/grape/util/lazy_object.rb +0 -43
- data/lib/grape/util/lazy_value.rb +0 -91
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'stackable_values'
|
4
|
-
|
5
3
|
module Grape
|
6
4
|
module Util
|
7
5
|
class ReverseStackableValues < StackableValues
|
@@ -10,10 +8,7 @@ module Grape
|
|
10
8
|
def concat_values(inherited_value, new_value)
|
11
9
|
return inherited_value unless new_value
|
12
10
|
|
13
|
-
|
14
|
-
value.concat(new_value)
|
15
|
-
value.concat(inherited_value)
|
16
|
-
end
|
11
|
+
new_value + inherited_value
|
17
12
|
end
|
18
13
|
end
|
19
14
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'base_inheritable'
|
4
|
-
|
5
3
|
module Grape
|
6
4
|
module Util
|
7
5
|
class StackableValues < BaseInheritable
|
@@ -31,10 +29,7 @@ module Grape
|
|
31
29
|
def concat_values(inherited_value, new_value)
|
32
30
|
return inherited_value unless new_value
|
33
31
|
|
34
|
-
|
35
|
-
value.concat(inherited_value)
|
36
|
-
value.concat(new_value)
|
37
|
-
end
|
32
|
+
inherited_value + new_value
|
38
33
|
end
|
39
34
|
end
|
40
35
|
end
|
@@ -56,19 +56,19 @@ module Grape
|
|
56
56
|
def self.nested_settings_methods(setting_name, new_config_class)
|
57
57
|
new_config_class.class_eval do
|
58
58
|
setting_name.each_pair do |key, value|
|
59
|
-
define_method "#{key}_context" do
|
59
|
+
define_method :"#{key}_context" do
|
60
60
|
@contexts[key] ||= Grape::Util::StrictHashConfiguration.config_class(*value).new
|
61
61
|
end
|
62
62
|
|
63
63
|
define_method key do |&block|
|
64
|
-
send("#{key}_context").instance_exec(&block)
|
64
|
+
send(:"#{key}_context").instance_exec(&block)
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
68
|
define_method :to_hash do
|
69
69
|
@settings.to_hash.merge(
|
70
70
|
setting_name.each_key.with_object({}) do |k, merge_hash|
|
71
|
-
merge_hash[k] = send("#{k}_context").to_hash
|
71
|
+
merge_hash[k] = send(:"#{k}_context").to_hash
|
72
72
|
end
|
73
73
|
)
|
74
74
|
end
|
@@ -2,56 +2,58 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Validations
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
5
|
+
# Documents parameters of an endpoint. If documentation isn't needed (for instance, it is an
|
6
|
+
# internal API), the class only cleans up attributes to avoid junk in RAM.
|
7
|
+
|
8
|
+
class AttributesDoc
|
9
|
+
attr_accessor :type, :values
|
10
|
+
|
11
|
+
# @param api [Grape::API::Instance]
|
12
|
+
# @param scope [Validations::ParamsScope]
|
13
|
+
def initialize(api, scope)
|
14
|
+
@api = api
|
15
|
+
@scope = scope
|
16
|
+
@type = type
|
17
|
+
end
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
def extract_details(validations)
|
20
|
+
details[:required] = validations.key?(:presence)
|
21
21
|
|
22
|
-
|
22
|
+
desc = validations.delete(:desc) || validations.delete(:description)
|
23
23
|
|
24
|
-
|
24
|
+
details[:desc] = desc if desc
|
25
25
|
|
26
|
-
|
26
|
+
documentation = validations.delete(:documentation)
|
27
27
|
|
28
|
-
|
28
|
+
details[:documentation] = documentation if documentation
|
29
29
|
|
30
|
-
|
31
|
-
end
|
30
|
+
details[:default] = validations[:default] if validations.key?(:default)
|
32
31
|
|
33
|
-
|
34
|
-
|
32
|
+
details[:min_length] = validations[:length][:min] if validations.key?(:length) && validations[:length].key?(:min)
|
33
|
+
details[:max_length] = validations[:length][:max] if validations.key?(:length) && validations[:length].key?(:max)
|
34
|
+
end
|
35
35
|
|
36
|
-
|
37
|
-
|
36
|
+
def document(attrs)
|
37
|
+
return if @api.namespace_inheritable(:do_not_document)
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
end
|
39
|
+
details[:type] = type.to_s if type
|
40
|
+
details[:values] = values if values
|
42
41
|
|
43
|
-
|
42
|
+
documented_attrs = attrs.each_with_object({}) do |name, memo|
|
43
|
+
memo[@scope.full_name(name)] = details
|
44
44
|
end
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
@api.namespace_stackable(:params, documented_attrs)
|
47
|
+
end
|
48
|
+
|
49
|
+
def required
|
50
|
+
details[:required]
|
51
|
+
end
|
49
52
|
|
50
|
-
|
53
|
+
protected
|
51
54
|
|
52
|
-
|
53
|
-
|
54
|
-
end
|
55
|
+
def details
|
56
|
+
@details ||= {}
|
55
57
|
end
|
56
58
|
end
|
57
59
|
end
|
@@ -21,6 +21,7 @@ module Grape
|
|
21
21
|
private
|
22
22
|
|
23
23
|
def do_each(params_to_process, parent_indicies = [], &block)
|
24
|
+
@scope.reset_index # gets updated depending on the size of params_to_process
|
24
25
|
params_to_process.each_with_index do |resource_params, index|
|
25
26
|
# when we get arrays of arrays it means that target element located inside array
|
26
27
|
# we need this because we want to know parent arrays indicies
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Validations
|
5
|
+
class ContractScope
|
6
|
+
# Declare the contract to be used for the endpoint's parameters.
|
7
|
+
# @param api [API] the API endpoint to modify.
|
8
|
+
# @param contract the contract or schema to be used for validation. Optional.
|
9
|
+
# @yield a block yielding a new schema class. Optional.
|
10
|
+
def initialize(api, contract = nil, &block)
|
11
|
+
# When block is passed, the first arg is either schema or nil.
|
12
|
+
contract = Dry::Schema.Params(parent: contract, &block) if block
|
13
|
+
|
14
|
+
if contract.respond_to?(:schema)
|
15
|
+
# It's a Dry::Validation::Contract, then.
|
16
|
+
contract = contract.new
|
17
|
+
key_map = contract.schema.key_map
|
18
|
+
else
|
19
|
+
# Dry::Schema::Processor, hopefully.
|
20
|
+
key_map = contract.key_map
|
21
|
+
end
|
22
|
+
|
23
|
+
api.namespace_stackable(:contract_key_map, key_map)
|
24
|
+
|
25
|
+
validator_options = {
|
26
|
+
validator_class: Validator,
|
27
|
+
opts: { schema: contract }
|
28
|
+
}
|
29
|
+
|
30
|
+
api.namespace_stackable(:validations, validator_options)
|
31
|
+
end
|
32
|
+
|
33
|
+
class Validator
|
34
|
+
attr_reader :schema
|
35
|
+
|
36
|
+
def initialize(*_args, schema:)
|
37
|
+
@schema = schema
|
38
|
+
end
|
39
|
+
|
40
|
+
# Validates a given request.
|
41
|
+
# @param request [Grape::Request] the request currently being handled
|
42
|
+
# @raise [Grape::Exceptions::ValidationArrayErrors] if validation failed
|
43
|
+
# @return [void]
|
44
|
+
def validate(request)
|
45
|
+
res = schema.call(request.params)
|
46
|
+
|
47
|
+
if res.success?
|
48
|
+
request.params.deep_merge!(res.to_h)
|
49
|
+
return
|
50
|
+
end
|
51
|
+
|
52
|
+
errors = []
|
53
|
+
|
54
|
+
res.errors.messages.each do |message|
|
55
|
+
full_name = message.path.first.to_s
|
56
|
+
|
57
|
+
full_name += "[#{message.path[1..].join('][')}]" if message.path.size > 1
|
58
|
+
|
59
|
+
errors << Grape::Exceptions::Validation.new(params: [full_name], message: message.text)
|
60
|
+
end
|
61
|
+
|
62
|
+
raise Grape::Exceptions::ValidationArrayErrors.new(errors)
|
63
|
+
end
|
64
|
+
|
65
|
+
def fail_fast?
|
66
|
+
false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'attributes_doc'
|
4
|
-
|
5
3
|
module Grape
|
6
4
|
module Validations
|
7
5
|
class ParamsScope
|
@@ -95,9 +93,7 @@ module Grape
|
|
95
93
|
|
96
94
|
def meets_dependency?(params, request_params)
|
97
95
|
return true unless @dependent_on
|
98
|
-
|
99
96
|
return false if @parent.present? && !@parent.meets_dependency?(@parent.params(request_params), request_params)
|
100
|
-
|
101
97
|
return params.any? { |param| meets_dependency?(param, request_params) } if params.is_a?(Array)
|
102
98
|
|
103
99
|
meets_hash_dependency?(params)
|
@@ -105,7 +101,6 @@ module Grape
|
|
105
101
|
|
106
102
|
def attr_meets_dependency?(params)
|
107
103
|
return true unless @dependent_on
|
108
|
-
|
109
104
|
return false if @parent.present? && !@parent.attr_meets_dependency?(params)
|
110
105
|
|
111
106
|
meets_hash_dependency?(params)
|
@@ -171,6 +166,10 @@ module Grape
|
|
171
166
|
!@optional
|
172
167
|
end
|
173
168
|
|
169
|
+
def reset_index
|
170
|
+
@index = nil
|
171
|
+
end
|
172
|
+
|
174
173
|
protected
|
175
174
|
|
176
175
|
# Adds a parameter declaration to our list of validations.
|
@@ -211,11 +210,11 @@ module Grape
|
|
211
210
|
|
212
211
|
def require_required_and_optional_fields(context, opts)
|
213
212
|
if context == :all
|
214
|
-
optional_fields = Array(opts[:except])
|
215
|
-
required_fields = opts[:using].keys
|
213
|
+
optional_fields = Array.wrap(opts[:except])
|
214
|
+
required_fields = opts[:using].keys.delete_if { |f| optional_fields.include?(f) }
|
216
215
|
else # context == :none
|
217
|
-
required_fields = Array(opts[:except])
|
218
|
-
optional_fields = opts[:using].keys
|
216
|
+
required_fields = Array.wrap(opts[:except])
|
217
|
+
optional_fields = opts[:using].keys.delete_if { |f| required_fields.include?(f) }
|
219
218
|
end
|
220
219
|
required_fields.each do |field|
|
221
220
|
field_opts = opts[:using][field]
|
@@ -231,7 +230,10 @@ module Grape
|
|
231
230
|
|
232
231
|
def require_optional_fields(context, opts)
|
233
232
|
optional_fields = opts[:using].keys
|
234
|
-
|
233
|
+
unless context == :all
|
234
|
+
except_fields = Array.wrap(opts[:except])
|
235
|
+
optional_fields.delete_if { |f| except_fields.include?(f) }
|
236
|
+
end
|
235
237
|
optional_fields.each do |field|
|
236
238
|
field_opts = opts[:using][field]
|
237
239
|
optional(field, field_opts) if field_opts
|
@@ -266,6 +268,7 @@ module Grape
|
|
266
268
|
parent: self,
|
267
269
|
optional: optional,
|
268
270
|
type: type || Array,
|
271
|
+
group: @group,
|
269
272
|
&block
|
270
273
|
)
|
271
274
|
end
|
@@ -295,12 +298,7 @@ module Grape
|
|
295
298
|
# `optional` invocation that opened this scope.
|
296
299
|
# @yield parameter scope
|
297
300
|
def new_group_scope(attrs, &block)
|
298
|
-
self.class.new(
|
299
|
-
api: @api,
|
300
|
-
parent: self,
|
301
|
-
group: attrs.first,
|
302
|
-
&block
|
303
|
-
)
|
301
|
+
self.class.new(api: @api, parent: self, group: attrs.first, &block)
|
304
302
|
end
|
305
303
|
|
306
304
|
# Pushes declared params to parent or settings
|
@@ -463,8 +461,7 @@ module Grape
|
|
463
461
|
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :values, values) if values && !values.is_a?(Proc) && !Array(default).all? { |def_val| values.include?(def_val) }
|
464
462
|
|
465
463
|
if except_values && !except_values.is_a?(Proc) && Array(default).any? { |def_val| except_values.include?(def_val) }
|
466
|
-
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :except, except_values)
|
467
|
-
|
464
|
+
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :except, except_values)
|
468
465
|
end
|
469
466
|
|
470
467
|
return unless excepts && !excepts.is_a?(Proc)
|
@@ -1,94 +1,92 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'array_coercer'
|
4
|
-
require_relative 'set_coercer'
|
5
|
-
require_relative 'primitive_coercer'
|
6
|
-
|
7
3
|
module Grape
|
8
4
|
module Validations
|
9
5
|
module Types
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
6
|
+
module BuildCoercer
|
7
|
+
# Chooses the best coercer for the given type. For example, if the type
|
8
|
+
# is Integer, it will return a coercer which will be able to coerce a value
|
9
|
+
# to the integer.
|
10
|
+
#
|
11
|
+
# There are a few very special coercers which might be returned.
|
12
|
+
#
|
13
|
+
# +Grape::Types::MultipleTypeCoercer+ is a coercer which is returned when
|
14
|
+
# the given type implies values in an array with different types.
|
15
|
+
# For example, +[Integer, String]+ allows integer and string values in
|
16
|
+
# an array.
|
17
|
+
#
|
18
|
+
# +Grape::Types::CustomTypeCoercer+ is a coercer which is returned when
|
19
|
+
# a method is specified by a user with +coerce_with+ option or the user
|
20
|
+
# specifies a custom type which implements requirments of
|
21
|
+
# +Grape::Types::CustomTypeCoercer+.
|
22
|
+
#
|
23
|
+
# +Grape::Types::CustomTypeCollectionCoercer+ is a very similar to the
|
24
|
+
# previous one, but it expects an array or set of values having a custom
|
25
|
+
# type implemented by the user.
|
26
|
+
#
|
27
|
+
# There is also a group of custom types implemented by Grape, check
|
28
|
+
# +Grape::Validations::Types::SPECIAL+ to get the full list.
|
29
|
+
#
|
30
|
+
# @param type [Class] the type to which input strings
|
31
|
+
# should be coerced
|
32
|
+
# @param method [Class,#call] the coercion method to use
|
33
|
+
# @return [Object] object to be used
|
34
|
+
# for coercion and type validation
|
35
|
+
def self.build_coercer(type, method: nil, strict: false)
|
36
|
+
cache_instance(type, method, strict) do
|
37
|
+
create_coercer_instance(type, method, strict)
|
38
|
+
end
|
41
39
|
end
|
42
|
-
end
|
43
40
|
|
44
|
-
|
45
|
-
|
46
|
-
|
41
|
+
def self.create_coercer_instance(type, method, strict)
|
42
|
+
# Maps a custom type provided by Grape, it doesn't map types wrapped by collections!!!
|
43
|
+
type = Types.map_special(type)
|
47
44
|
|
48
|
-
|
49
|
-
|
50
|
-
|
45
|
+
# Use a special coercer for multiply-typed parameters.
|
46
|
+
if Types.multiple?(type)
|
47
|
+
MultipleTypeCoercer.new(type, method)
|
51
48
|
|
52
|
-
|
53
|
-
|
54
|
-
|
49
|
+
# Use a special coercer for custom types and coercion methods.
|
50
|
+
elsif method || Types.custom?(type)
|
51
|
+
CustomTypeCoercer.new(type, method)
|
55
52
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
53
|
+
# Special coercer for collections of types that implement a parse method.
|
54
|
+
# CustomTypeCoercer (above) already handles such types when an explicit coercion
|
55
|
+
# method is supplied.
|
56
|
+
elsif Types.collection_of_custom?(type)
|
57
|
+
Types::CustomTypeCollectionCoercer.new(
|
58
|
+
Types.map_special(type.first), type.is_a?(Set)
|
59
|
+
)
|
60
|
+
else
|
61
|
+
DryTypeCoercer.coercer_instance_for(type, strict)
|
62
|
+
end
|
65
63
|
end
|
66
|
-
end
|
67
64
|
|
68
|
-
|
69
|
-
|
65
|
+
def self.cache_instance(type, method, strict, &_block)
|
66
|
+
key = cache_key(type, method, strict)
|
70
67
|
|
71
|
-
|
68
|
+
return @__cache[key] if @__cache.key?(key)
|
72
69
|
|
73
|
-
|
70
|
+
instance = yield
|
74
71
|
|
75
|
-
|
76
|
-
|
77
|
-
|
72
|
+
@__cache_write_lock.synchronize do
|
73
|
+
@__cache[key] = instance
|
74
|
+
end
|
78
75
|
|
79
|
-
|
80
|
-
|
76
|
+
instance
|
77
|
+
end
|
81
78
|
|
82
|
-
|
83
|
-
|
84
|
-
|
79
|
+
def self.cache_key(type, method, strict)
|
80
|
+
[type, method, strict].each_with_object(+'_') do |val, memo|
|
81
|
+
next if val.nil?
|
85
82
|
|
86
|
-
|
83
|
+
memo << '_' << val.to_s
|
84
|
+
end
|
87
85
|
end
|
88
|
-
end
|
89
86
|
|
90
|
-
|
91
|
-
|
87
|
+
instance_variable_set(:@__cache, {})
|
88
|
+
instance_variable_set(:@__cache_write_lock, Mutex.new)
|
89
|
+
end
|
92
90
|
end
|
93
91
|
end
|
94
92
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'dry-types'
|
4
|
-
|
5
3
|
module DryTypes
|
6
4
|
# Call +Dry.Types()+ to add all registered types to +DryTypes+ which is
|
7
5
|
# a container in this case. Check documentation for more information
|
@@ -24,9 +22,7 @@ module Grape
|
|
24
22
|
# collection_coercer_for(Array)
|
25
23
|
# #=> Grape::Validations::Types::ArrayCoercer
|
26
24
|
def collection_coercer_for(type)
|
27
|
-
|
28
|
-
DryTypeCoercer.collection_coercers[type] = Grape::Validations::Types.const_get("#{type.name.camelize}Coercer")
|
29
|
-
end
|
25
|
+
Grape::Validations::Types.const_get(:"#{type.name.camelize}Coercer")
|
30
26
|
end
|
31
27
|
|
32
28
|
# Returns an instance of a coercer for a given type
|
@@ -37,12 +33,6 @@ module Grape
|
|
37
33
|
# so we need to figure out the actual type
|
38
34
|
collection_coercer_for(type.class).new(type, strict)
|
39
35
|
end
|
40
|
-
|
41
|
-
protected
|
42
|
-
|
43
|
-
def collection_coercers
|
44
|
-
@collection_coercers ||= {}
|
45
|
-
end
|
46
36
|
end
|
47
37
|
|
48
38
|
def initialize(type, strict = false)
|
@@ -11,7 +11,11 @@ module Grape
|
|
11
11
|
|
12
12
|
def validate_param!(attr_name, params)
|
13
13
|
params[attr_name] = if @default.is_a? Proc
|
14
|
-
@default.
|
14
|
+
if @default.parameters.empty?
|
15
|
+
@default.call
|
16
|
+
else
|
17
|
+
@default.call(params)
|
18
|
+
end
|
15
19
|
elsif @default.frozen? || !@default.duplicable?
|
16
20
|
@default
|
17
21
|
else
|
@@ -7,7 +7,7 @@ module Grape
|
|
7
7
|
def validate_params!(params)
|
8
8
|
keys = keys_in_common(params)
|
9
9
|
return if keys.length == 1
|
10
|
-
raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:exactly_one)) if keys.
|
10
|
+
raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:exactly_one)) if keys.empty?
|
11
11
|
|
12
12
|
raise Grape::Exceptions::Validation.new(params: keys, message: message(:mutual_exclusion))
|
13
13
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Validations
|
5
|
+
module Validators
|
6
|
+
class LengthValidator < Base
|
7
|
+
def initialize(attrs, options, required, scope, **opts)
|
8
|
+
@min = options[:min]
|
9
|
+
@max = options[:max]
|
10
|
+
|
11
|
+
super
|
12
|
+
|
13
|
+
raise ArgumentError, 'min must be an integer greater than or equal to zero' if !@min.nil? && (!@min.is_a?(Integer) || @min.negative?)
|
14
|
+
raise ArgumentError, 'max must be an integer greater than or equal to zero' if !@max.nil? && (!@max.is_a?(Integer) || @max.negative?)
|
15
|
+
raise ArgumentError, "min #{@min} cannot be greater than max #{@max}" if !@min.nil? && !@max.nil? && @min > @max
|
16
|
+
end
|
17
|
+
|
18
|
+
def validate_param!(attr_name, params)
|
19
|
+
param = params[attr_name]
|
20
|
+
|
21
|
+
raise ArgumentError, "parameter #{param} does not support #length" unless param.respond_to?(:length)
|
22
|
+
|
23
|
+
return unless (!@min.nil? && param.length < @min) || (!@max.nil? && param.length > @max)
|
24
|
+
|
25
|
+
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: build_message)
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_message
|
29
|
+
if options_key?(:message)
|
30
|
+
@option[:message]
|
31
|
+
elsif @min && @max
|
32
|
+
format I18n.t(:length, scope: 'grape.errors.messages'), min: @min, max: @max
|
33
|
+
elsif @min
|
34
|
+
format I18n.t(:length_min, scope: 'grape.errors.messages'), min: @min
|
35
|
+
else
|
36
|
+
format I18n.t(:length_max, scope: 'grape.errors.messages'), max: @max
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|