grape 0.14.0 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of grape might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +32 -4
- data/Gemfile.lock +13 -13
- data/README.md +290 -12
- data/UPGRADING.md +68 -1
- data/gemfiles/rails_3.gemfile +1 -1
- data/lib/grape.rb +8 -2
- data/lib/grape/api.rb +40 -34
- data/lib/grape/dsl/configuration.rb +2 -115
- data/lib/grape/dsl/desc.rb +101 -0
- data/lib/grape/dsl/headers.rb +16 -0
- data/lib/grape/dsl/helpers.rb +5 -9
- data/lib/grape/dsl/inside_route.rb +3 -11
- data/lib/grape/dsl/logger.rb +20 -0
- data/lib/grape/dsl/parameters.rb +12 -10
- data/lib/grape/dsl/request_response.rb +17 -4
- data/lib/grape/dsl/routing.rb +24 -7
- data/lib/grape/dsl/settings.rb +8 -2
- data/lib/grape/endpoint.rb +30 -26
- data/lib/grape/error_formatter.rb +31 -0
- data/lib/grape/error_formatter/base.rb +0 -28
- data/lib/grape/error_formatter/json.rb +13 -2
- data/lib/grape/error_formatter/txt.rb +3 -1
- data/lib/grape/error_formatter/xml.rb +3 -1
- data/lib/grape/exceptions/base.rb +11 -4
- data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
- data/lib/grape/exceptions/invalid_accept_header.rb +1 -1
- data/lib/grape/exceptions/invalid_formatter.rb +1 -1
- data/lib/grape/exceptions/invalid_message_body.rb +1 -1
- data/lib/grape/exceptions/invalid_version_header.rb +1 -1
- data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -1
- data/lib/grape/exceptions/method_not_allowed.rb +10 -0
- data/lib/grape/exceptions/missing_group_type.rb +1 -1
- data/lib/grape/exceptions/missing_mime_type.rb +1 -1
- data/lib/grape/exceptions/missing_option.rb +1 -1
- data/lib/grape/exceptions/missing_vendor_option.rb +1 -1
- data/lib/grape/exceptions/unknown_options.rb +1 -1
- data/lib/grape/exceptions/unknown_parameter.rb +1 -1
- data/lib/grape/exceptions/unknown_validator.rb +1 -1
- data/lib/grape/exceptions/unsupported_group_type.rb +1 -1
- data/lib/grape/exceptions/validation.rb +2 -1
- data/lib/grape/formatter.rb +31 -0
- data/lib/grape/middleware/base.rb +28 -2
- data/lib/grape/middleware/error.rb +24 -1
- data/lib/grape/middleware/formatter.rb +4 -3
- data/lib/grape/middleware/versioner/param.rb +13 -2
- data/lib/grape/parser.rb +29 -0
- data/lib/grape/util/sendfile_response.rb +19 -0
- data/lib/grape/util/strict_hash_configuration.rb +1 -1
- data/lib/grape/validations/params_scope.rb +39 -9
- data/lib/grape/validations/types.rb +16 -0
- data/lib/grape/validations/validators/all_or_none.rb +1 -1
- data/lib/grape/validations/validators/allow_blank.rb +2 -2
- data/lib/grape/validations/validators/at_least_one_of.rb +1 -1
- data/lib/grape/validations/validators/base.rb +26 -0
- data/lib/grape/validations/validators/coerce.rb +16 -14
- data/lib/grape/validations/validators/default.rb +1 -1
- data/lib/grape/validations/validators/exactly_one_of.rb +10 -1
- data/lib/grape/validations/validators/mutual_exclusion.rb +1 -1
- data/lib/grape/validations/validators/presence.rb +1 -1
- data/lib/grape/validations/validators/regexp.rb +2 -2
- data/lib/grape/validations/validators/values.rb +2 -2
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api/custom_validations_spec.rb +156 -21
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +38 -0
- data/spec/grape/api/optional_parameters_in_route_spec.rb +43 -0
- data/spec/grape/api/required_parameters_in_route_spec.rb +37 -0
- data/spec/grape/api_spec.rb +118 -60
- data/spec/grape/dsl/configuration_spec.rb +0 -75
- data/spec/grape/dsl/desc_spec.rb +77 -0
- data/spec/grape/dsl/headers_spec.rb +32 -0
- data/spec/grape/dsl/inside_route_spec.rb +0 -18
- data/spec/grape/dsl/logger_spec.rb +26 -0
- data/spec/grape/dsl/parameters_spec.rb +13 -7
- data/spec/grape/dsl/request_response_spec.rb +17 -3
- data/spec/grape/dsl/routing_spec.rb +8 -1
- data/spec/grape/dsl/settings_spec.rb +42 -0
- data/spec/grape/endpoint_spec.rb +60 -9
- data/spec/grape/exceptions/validation_errors_spec.rb +2 -2
- data/spec/grape/exceptions/validation_spec.rb +7 -0
- data/spec/grape/integration/rack_sendfile_spec.rb +44 -0
- data/spec/grape/middleware/base_spec.rb +100 -0
- data/spec/grape/middleware/exception_spec.rb +1 -2
- data/spec/grape/middleware/formatter_spec.rb +12 -2
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +1 -1
- data/spec/grape/middleware/versioner/header_spec.rb +11 -1
- data/spec/grape/middleware/versioner/param_spec.rb +105 -1
- data/spec/grape/validations/params_scope_spec.rb +77 -0
- data/spec/grape/validations/validators/allow_blank_spec.rb +277 -0
- data/spec/grape/validations/validators/coerce_spec.rb +91 -0
- data/spec/grape/validations/validators/default_spec.rb +6 -0
- data/spec/grape/validations/validators/presence_spec.rb +27 -0
- data/spec/grape/validations/validators/regexp_spec.rb +36 -0
- data/spec/grape/validations/validators/values_spec.rb +44 -0
- data/spec/grape/validations_spec.rb +149 -4
- data/spec/spec_helper.rb +1 -0
- metadata +26 -5
- data/lib/grape/formatter/base.rb +0 -31
- data/lib/grape/parser/base.rb +0 -29
- data/pkg/grape-0.13.0.gem +0 -0
@@ -19,6 +19,7 @@ module Grape
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def after
|
22
|
+
return unless @app_response
|
22
23
|
status, headers, bodies = *@app_response
|
23
24
|
|
24
25
|
if Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status)
|
@@ -34,7 +35,7 @@ module Grape
|
|
34
35
|
headers = ensure_content_type(headers)
|
35
36
|
|
36
37
|
if bodies.is_a?(Grape::Util::FileResponse)
|
37
|
-
|
38
|
+
Grape::Util::SendfileResponse.new([], status, headers) do |resp|
|
38
39
|
resp.body = bodies.file
|
39
40
|
end
|
40
41
|
else
|
@@ -49,7 +50,7 @@ module Grape
|
|
49
50
|
|
50
51
|
def fetch_formatter(headers, options)
|
51
52
|
api_format = mime_types[headers[Grape::Http::Headers::CONTENT_TYPE]] || env[Grape::Env::API_FORMAT]
|
52
|
-
Grape::Formatter
|
53
|
+
Grape::Formatter.formatter_for(api_format, options)
|
53
54
|
end
|
54
55
|
|
55
56
|
# Set the content type header for the API format if it is not already present.
|
@@ -92,7 +93,7 @@ module Grape
|
|
92
93
|
fmt = mime_types[request.media_type] if request.media_type
|
93
94
|
fmt ||= options[:default_format]
|
94
95
|
if content_type_for(fmt)
|
95
|
-
parser = Grape::Parser
|
96
|
+
parser = Grape::Parser.parser_for fmt, options
|
96
97
|
if parser
|
97
98
|
begin
|
98
99
|
body = (env[Grape::Env::API_REQUEST_BODY] = parser.call(body, env))
|
@@ -21,18 +21,29 @@ module Grape
|
|
21
21
|
class Param < Base
|
22
22
|
def default_options
|
23
23
|
{
|
24
|
-
|
24
|
+
version_options: {
|
25
|
+
parameter: 'apiver'.freeze
|
26
|
+
}
|
25
27
|
}
|
26
28
|
end
|
27
29
|
|
28
30
|
def before
|
29
|
-
paramkey = options[:parameter]
|
30
31
|
potential_version = Rack::Utils.parse_nested_query(env[Grape::Http::Headers::QUERY_STRING])[paramkey]
|
31
32
|
return if potential_version.nil?
|
32
33
|
throw :error, status: 404, message: '404 API Version Not Found', headers: { Grape::Http::Headers::X_CASCADE => 'pass' } if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
|
33
34
|
env[Grape::Env::API_VERSION] = potential_version
|
34
35
|
env[Grape::Env::RACK_REQUEST_QUERY_HASH].delete(paramkey) if env.key? Grape::Env::RACK_REQUEST_QUERY_HASH
|
35
36
|
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def paramkey
|
41
|
+
version_options[:parameter] || default_options[:version_options][:parameter]
|
42
|
+
end
|
43
|
+
|
44
|
+
def version_options
|
45
|
+
options[:version_options]
|
46
|
+
end
|
36
47
|
end
|
37
48
|
end
|
38
49
|
end
|
data/lib/grape/parser.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Grape
|
2
|
+
module Parser
|
3
|
+
class << self
|
4
|
+
def builtin_parsers
|
5
|
+
{
|
6
|
+
json: Grape::Parser::Json,
|
7
|
+
jsonapi: Grape::Parser::Json,
|
8
|
+
xml: Grape::Parser::Xml
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def parsers(options)
|
13
|
+
builtin_parsers.merge(options[:parsers] || {})
|
14
|
+
end
|
15
|
+
|
16
|
+
def parser_for(api_format, options = {})
|
17
|
+
spec = parsers(options)[api_format]
|
18
|
+
case spec
|
19
|
+
when nil
|
20
|
+
nil
|
21
|
+
when Symbol
|
22
|
+
method(spec)
|
23
|
+
else
|
24
|
+
spec
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Grape
|
2
|
+
module Util
|
3
|
+
# Response should respond to to_path method
|
4
|
+
# for using Rack::SendFile middleware
|
5
|
+
class SendfileResponse < Rack::Response
|
6
|
+
def respond_to?(method_name, include_all = false)
|
7
|
+
if method_name == :to_path
|
8
|
+
@body.respond_to?(:to_path, include_all)
|
9
|
+
else
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_path
|
15
|
+
@body.to_path
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -36,7 +36,9 @@ module Grape
|
|
36
36
|
# validated
|
37
37
|
def should_validate?(parameters)
|
38
38
|
return false if @optional && params(parameters).respond_to?(:all?) && params(parameters).all?(&:blank?)
|
39
|
-
|
39
|
+
@dependent_on.each do |dependency|
|
40
|
+
return false if params(parameters).try(:[], dependency).blank?
|
41
|
+
end if @dependent_on
|
40
42
|
return true if parent.nil?
|
41
43
|
parent.should_validate?(parameters)
|
42
44
|
end
|
@@ -90,7 +92,11 @@ module Grape
|
|
90
92
|
# Adds a parameter declaration to our list of validations.
|
91
93
|
# @param attrs [Array] (see Grape::DSL::Parameters#requires)
|
92
94
|
def push_declared_params(attrs)
|
93
|
-
|
95
|
+
if lateral?
|
96
|
+
@parent.push_declared_params(attrs)
|
97
|
+
else
|
98
|
+
@declared_params.concat attrs
|
99
|
+
end
|
94
100
|
end
|
95
101
|
|
96
102
|
private
|
@@ -141,11 +147,18 @@ module Grape
|
|
141
147
|
type = attrs[1] ? attrs[1][:type] : nil
|
142
148
|
if attrs.first && !optional
|
143
149
|
fail Grape::Exceptions::MissingGroupTypeError.new if type.nil?
|
144
|
-
fail Grape::Exceptions::UnsupportedGroupTypeError.new unless
|
150
|
+
fail Grape::Exceptions::UnsupportedGroupTypeError.new unless Grape::Validations::Types.group?(type)
|
145
151
|
end
|
146
152
|
|
147
153
|
opts = attrs[1] || { type: Array }
|
148
|
-
|
154
|
+
|
155
|
+
self.class.new(
|
156
|
+
api: @api,
|
157
|
+
element: attrs.first,
|
158
|
+
parent: self,
|
159
|
+
optional: optional,
|
160
|
+
type: opts[:type],
|
161
|
+
&block)
|
149
162
|
end
|
150
163
|
|
151
164
|
# Returns a new parameter scope, not nested under any current-level param
|
@@ -191,7 +204,7 @@ module Grape
|
|
191
204
|
default = validations[:default]
|
192
205
|
doc_attrs[:default] = default if validations.key?(:default)
|
193
206
|
|
194
|
-
values = validations[:values]
|
207
|
+
values = (options_key?(:values, :value, validations)) ? validations[:values][:value] : validations[:values]
|
195
208
|
doc_attrs[:values] = values if values
|
196
209
|
|
197
210
|
coerce_type = guess_coerce_type(coerce_type, values)
|
@@ -211,6 +224,7 @@ module Grape
|
|
211
224
|
if validations.key?(:presence) && validations[:presence]
|
212
225
|
validate('presence', validations[:presence], attrs, doc_attrs)
|
213
226
|
validations.delete(:presence)
|
227
|
+
validations.delete(:message) if validations.key?(:message)
|
214
228
|
end
|
215
229
|
|
216
230
|
# Before we run the rest of the validators, let's handle
|
@@ -241,8 +255,12 @@ module Grape
|
|
241
255
|
fail ArgumentError, ':type may not be supplied with :types'
|
242
256
|
end
|
243
257
|
|
244
|
-
validations[:coerce] = validations[:type] if validations.key?(:type)
|
245
|
-
validations[:
|
258
|
+
validations[:coerce] = (options_key?(:type, :value, validations) ? validations[:type][:value] : validations[:type]) if validations.key?(:type)
|
259
|
+
validations[:coerce_message] = (options_key?(:type, :message, validations) ? validations[:type][:message] : nil) if validations.key?(:type)
|
260
|
+
validations[:coerce] = (options_key?(:types, :value, validations) ? validations[:types][:value] : validations[:types]) if validations.key?(:types)
|
261
|
+
validations[:coerce_message] = (options_key?(:types, :message, validations) ? validations[:types][:message] : nil) if validations.key?(:types)
|
262
|
+
|
263
|
+
validations.delete(:types) if validations.key?(:types)
|
246
264
|
|
247
265
|
coerce_type = validations[:coerce]
|
248
266
|
|
@@ -287,11 +305,13 @@ module Grape
|
|
287
305
|
|
288
306
|
coerce_options = {
|
289
307
|
type: validations[:coerce],
|
290
|
-
method: validations[:coerce_with]
|
308
|
+
method: validations[:coerce_with],
|
309
|
+
message: validations[:coerce_message]
|
291
310
|
}
|
292
311
|
validate('coerce', coerce_options, attrs, doc_attrs)
|
293
312
|
validations.delete(:coerce_with)
|
294
313
|
validations.delete(:coerce)
|
314
|
+
validations.delete(:coerce_message)
|
295
315
|
end
|
296
316
|
|
297
317
|
def guess_coerce_type(coerce_type, values)
|
@@ -303,7 +323,7 @@ module Grape
|
|
303
323
|
def check_incompatible_option_values(values, default)
|
304
324
|
return unless values && default
|
305
325
|
return if values.is_a?(Proc) || default.is_a?(Proc)
|
306
|
-
return if values.include?(default)
|
326
|
+
return if values.include?(default) || (Array(default) - Array(values)).empty?
|
307
327
|
fail Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :values, values)
|
308
328
|
end
|
309
329
|
|
@@ -329,6 +349,16 @@ module Grape
|
|
329
349
|
return unless value_types.any? { |v| !v.is_a?(coerce_type) }
|
330
350
|
fail Grape::Exceptions::IncompatibleOptionValues.new(:type, coerce_type, :values, values)
|
331
351
|
end
|
352
|
+
|
353
|
+
def extract_message_option(attrs)
|
354
|
+
return nil unless attrs.is_a?(Array)
|
355
|
+
opts = attrs.last.is_a?(Hash) ? attrs.pop : {}
|
356
|
+
(opts.key?(:message) && !opts[:message].nil?) ? opts.delete(:message) : nil
|
357
|
+
end
|
358
|
+
|
359
|
+
def options_key?(type, key, validations)
|
360
|
+
validations[type].respond_to?(:key?) && validations[type].key?(key) && !validations[type][key].nil?
|
361
|
+
end
|
332
362
|
end
|
333
363
|
end
|
334
364
|
end
|
@@ -63,6 +63,13 @@ module Grape
|
|
63
63
|
Rack::Multipart::UploadedFile => File
|
64
64
|
}
|
65
65
|
|
66
|
+
GROUPS = [
|
67
|
+
Array,
|
68
|
+
Hash,
|
69
|
+
JSON,
|
70
|
+
Array[JSON]
|
71
|
+
]
|
72
|
+
|
66
73
|
# Is the given class a primitive type as recognized by Grape?
|
67
74
|
#
|
68
75
|
# @param type [Class] type to check
|
@@ -126,6 +133,15 @@ module Grape
|
|
126
133
|
SPECIAL.key? type
|
127
134
|
end
|
128
135
|
|
136
|
+
# Is the declared type a supported group type?
|
137
|
+
# Currently supported group types are Array, Hash, JSON, and Array[JSON]
|
138
|
+
#
|
139
|
+
# @param type [Array<Class>,Class] type to check
|
140
|
+
# @return [Boolean] +true+ if the type is a supported group type
|
141
|
+
def self.group?(type)
|
142
|
+
GROUPS.include? type
|
143
|
+
end
|
144
|
+
|
129
145
|
# A valid custom type must implement a class-level `parse` method, taking
|
130
146
|
# one String argument and returning the parsed value in its correct type.
|
131
147
|
# @param type [Class] type to check
|
@@ -5,7 +5,7 @@ module Grape
|
|
5
5
|
def validate!(params)
|
6
6
|
super
|
7
7
|
if scope_requires_params && only_subset_present
|
8
|
-
fail Grape::Exceptions::Validation, params: all_keys,
|
8
|
+
fail Grape::Exceptions::Validation, params: all_keys, message: message(:all_or_none)
|
9
9
|
end
|
10
10
|
params
|
11
11
|
end
|
@@ -2,7 +2,7 @@ module Grape
|
|
2
2
|
module Validations
|
3
3
|
class AllowBlankValidator < Base
|
4
4
|
def validate_param!(attr_name, params)
|
5
|
-
return if @option || !params.is_a?(Hash)
|
5
|
+
return if (options_key?(:value) ? @option[:value] : @option) || !params.is_a?(Hash)
|
6
6
|
|
7
7
|
value = params[attr_name]
|
8
8
|
value = value.strip if value.respond_to?(:strip)
|
@@ -23,7 +23,7 @@ module Grape
|
|
23
23
|
|
24
24
|
return if value == false || value.present?
|
25
25
|
|
26
|
-
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
|
26
|
+
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:blank)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
@@ -5,7 +5,7 @@ module Grape
|
|
5
5
|
def validate!(params)
|
6
6
|
super
|
7
7
|
if scope_requires_params && no_exclusive_params_are_present
|
8
|
-
fail Grape::Exceptions::Validation, params: all_keys,
|
8
|
+
fail Grape::Exceptions::Validation, params: all_keys, message: message(:at_least_one)
|
9
9
|
end
|
10
10
|
params
|
11
11
|
end
|
@@ -17,6 +17,22 @@ module Grape
|
|
17
17
|
@scope = scope
|
18
18
|
end
|
19
19
|
|
20
|
+
# Validates a given request.
|
21
|
+
# @note This method must be thread-safe.
|
22
|
+
# @note Override #validate! unless you need to access the entire request.
|
23
|
+
# @param request [Grape::Request] the request currently being handled
|
24
|
+
# @raise [Grape::Exceptions::Validation] if validation failed
|
25
|
+
# @return [void]
|
26
|
+
def validate(request)
|
27
|
+
validate!(request.params)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Validates a given parameter hash.
|
31
|
+
# @note This method must be thread-safe.
|
32
|
+
# @note Override #validate iff you need to access the entire request.
|
33
|
+
# @param params [Hash] parameters to validate
|
34
|
+
# @raise [Grape::Exceptions::Validation] if validation failed
|
35
|
+
# @return [void]
|
20
36
|
def validate!(params)
|
21
37
|
attributes = AttributesIterator.new(self, @scope, params)
|
22
38
|
attributes.each do |resource_params, attr_name|
|
@@ -39,6 +55,16 @@ module Grape
|
|
39
55
|
short_name = convert_to_short_name(klass)
|
40
56
|
Validations.register_validator(short_name, klass)
|
41
57
|
end
|
58
|
+
|
59
|
+
def message(default_key = nil)
|
60
|
+
options = instance_variable_get(:@option)
|
61
|
+
options_key?(:message) ? options[:message] : default_key
|
62
|
+
end
|
63
|
+
|
64
|
+
def options_key?(key, options = nil)
|
65
|
+
options = instance_variable_get(:@option) if options.nil?
|
66
|
+
options.respond_to?(:key?) && options.key?(key) && !options[key].nil?
|
67
|
+
end
|
42
68
|
end
|
43
69
|
end
|
44
70
|
end
|
@@ -5,18 +5,31 @@ module Grape
|
|
5
5
|
|
6
6
|
module Validations
|
7
7
|
class CoerceValidator < Base
|
8
|
+
def initialize(*_args)
|
9
|
+
super
|
10
|
+
@converter = Types.build_coercer(type, @option[:method])
|
11
|
+
end
|
12
|
+
|
8
13
|
def validate_param!(attr_name, params)
|
9
|
-
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
|
14
|
+
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:coerce) unless params.is_a? Hash
|
10
15
|
new_value = coerce_value(params[attr_name])
|
11
16
|
if valid_type?(new_value)
|
12
17
|
params[attr_name] = new_value
|
13
18
|
else
|
14
|
-
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
|
19
|
+
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:coerce)
|
15
20
|
end
|
16
21
|
end
|
17
22
|
|
18
23
|
private
|
19
24
|
|
25
|
+
# @!attribute [r] converter
|
26
|
+
# Object that will be used for parameter coercion and type checking.
|
27
|
+
#
|
28
|
+
# See {Types.build_coercer}
|
29
|
+
#
|
30
|
+
# @return [Virtus::Attribute]
|
31
|
+
attr_reader :converter
|
32
|
+
|
20
33
|
def valid_type?(val)
|
21
34
|
# Special value to denote coercion failure
|
22
35
|
return false if val.instance_of?(Types::InvalidValue)
|
@@ -47,18 +60,7 @@ module Grape
|
|
47
60
|
#
|
48
61
|
# @return [Class]
|
49
62
|
def type
|
50
|
-
@option[:type]
|
51
|
-
end
|
52
|
-
|
53
|
-
# Create and cache the attribute object
|
54
|
-
# that will be used for parameter coercion
|
55
|
-
# and type checking.
|
56
|
-
#
|
57
|
-
# See {Types.build_coercer}
|
58
|
-
#
|
59
|
-
# @return [Virtus::Attribute]
|
60
|
-
def converter
|
61
|
-
@converter ||= Types.build_coercer(type, @option[:method])
|
63
|
+
@option[:type].is_a?(Hash) ? @option[:type][:value] : @option[:type]
|
62
64
|
end
|
63
65
|
end
|
64
66
|
end
|
@@ -15,7 +15,7 @@ module Grape
|
|
15
15
|
|
16
16
|
attrs = AttributesIterator.new(self, @scope, params)
|
17
17
|
attrs.each do |resource_params, attr_name|
|
18
|
-
if resource_params[attr_name].nil?
|
18
|
+
if resource_params.is_a?(Hash) && resource_params[attr_name].nil?
|
19
19
|
validate_param!(attr_name, resource_params)
|
20
20
|
end
|
21
21
|
end
|
@@ -5,11 +5,20 @@ module Grape
|
|
5
5
|
def validate!(params)
|
6
6
|
super
|
7
7
|
if scope_requires_params && none_of_restricted_params_is_present
|
8
|
-
fail Grape::Exceptions::Validation, params: all_keys,
|
8
|
+
fail Grape::Exceptions::Validation, params: all_keys, message: message(:exactly_one)
|
9
9
|
end
|
10
10
|
params
|
11
11
|
end
|
12
12
|
|
13
|
+
def message(default_key = nil)
|
14
|
+
options = instance_variable_get(:@option)
|
15
|
+
if options_key?(:message)
|
16
|
+
(options_key?(default_key, options[:message]) ? options[:message][default_key] : options[:message])
|
17
|
+
else
|
18
|
+
default_key
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
13
22
|
private
|
14
23
|
|
15
24
|
def none_of_restricted_params_is_present
|
@@ -7,7 +7,7 @@ module Grape
|
|
7
7
|
def validate!(params)
|
8
8
|
super
|
9
9
|
if two_or_more_exclusive_params_are_present
|
10
|
-
fail Grape::Exceptions::Validation, params: processing_keys_in_common,
|
10
|
+
fail Grape::Exceptions::Validation, params: processing_keys_in_common, message: message(:mutual_exclusion)
|
11
11
|
end
|
12
12
|
params
|
13
13
|
end
|
@@ -8,7 +8,7 @@ module Grape
|
|
8
8
|
|
9
9
|
def validate_param!(attr_name, params)
|
10
10
|
return if params.respond_to?(:key?) && params.key?(attr_name)
|
11
|
-
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
|
11
|
+
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:presence)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|