Skip to content

Commit

Permalink
Adding custom validation error messages
Browse files Browse the repository at this point in the history
  • Loading branch information
railsmith committed Feb 27, 2016
1 parent df3e4ae commit 1d46522
Show file tree
Hide file tree
Showing 38 changed files with 688 additions and 59 deletions.
10 changes: 5 additions & 5 deletions lib/grape/dsl/parameters.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def requires(*attrs, &block)
orig_attrs = attrs.clone

opts = attrs.extract_options!.clone
opts[:presence] = true
opts[:presence] = { value: true, message: opts[:message] }

if opts[:using]
require_required_and_optional_fields(attrs.first, opts)
Expand Down Expand Up @@ -137,25 +137,25 @@ def optional(*attrs, &block)
# Disallow the given parameters to be present in the same request.
# @param attrs [*Symbol] parameters to validate
def mutually_exclusive(*attrs)
validates(attrs, mutual_exclusion: true)
validates(attrs, mutual_exclusion: { value: true, message: extract_message_option(attrs) })
end

# Require exactly one of the given parameters to be present.
# @param (see #mutually_exclusive)
def exactly_one_of(*attrs)
validates(attrs, exactly_one_of: true)
validates(attrs, exactly_one_of: { value: true, message: extract_message_option(attrs) })
end

# Require at least one of the given parameters to be present.
# @param (see #mutually_exclusive)
def at_least_one_of(*attrs)
validates(attrs, at_least_one_of: true)
validates(attrs, at_least_one_of: { value: true, message: extract_message_option(attrs) })
end

# Require that either all given params are present, or none are.
# @param (see #mutually_exclusive)
def all_or_none_of(*attrs)
validates(attrs, all_or_none_of: true)
validates(attrs, all_or_none_of: { value: true, message: extract_message_option(attrs) })
end

# Define a block of validations which should be applied if and only if
Expand Down
15 changes: 11 additions & 4 deletions lib/grape/exceptions/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ def compose_message(key, attributes = {})
end

def problem(key, attributes)
translate_message("#{key}.problem", attributes)
translate_message("#{key}.problem".to_sym, attributes)
end

def summary(key, attributes)
translate_message("#{key}.summary", attributes)
translate_message("#{key}.summary".to_sym, attributes)
end

def resolution(key, attributes)
translate_message("#{key}.resolution", attributes)
translate_message("#{key}.resolution".to_sym, attributes)
end

def translate_attributes(keys, options = {})
Expand All @@ -60,7 +60,14 @@ def translate_attribute(key, options = {})
end

def translate_message(key, options = {})
translate("#{BASE_MESSAGES_KEY}.#{key}", options.reverse_merge(default: ''))
case key
when Symbol
translate("#{BASE_MESSAGES_KEY}.#{key}", options.reverse_merge(default: ''))
when Proc
key.call
else
key
end
end

def translate(key, options = {})
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/incompatible_option_values.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class IncompatibleOptionValues < Base
def initialize(option1, value1, option2, value2)
super(message: compose_message('incompatible_option_values', option1: option1, value1: value1, option2: option2, value2: value2))
super(message: compose_message(:incompatible_option_values, option1: option1, value1: value1, option2: option2, value2: value2))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/invalid_accept_header.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class InvalidAcceptHeader < Base
def initialize(message, headers)
super(message: compose_message('invalid_accept_header', message: message), status: 406, headers: headers)
super(message: compose_message(:invalid_accept_header, message: message), status: 406, headers: headers)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/invalid_formatter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class InvalidFormatter < Base
def initialize(klass, to_format)
super(message: compose_message('invalid_formatter', klass: klass, to_format: to_format))
super(message: compose_message(:invalid_formatter, klass: klass, to_format: to_format))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/invalid_message_body.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class InvalidMessageBody < Base
def initialize(body_format)
super(message: compose_message('invalid_message_body', body_format: body_format), status: 400)
super(message: compose_message(:invalid_message_body, body_format: body_format), status: 400)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/invalid_version_header.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class InvalidVersionHeader < Base
def initialize(message, headers)
super(message: compose_message('invalid_version_header', message: message), status: 406, headers: headers)
super(message: compose_message(:invalid_version_header, message: message), status: 406, headers: headers)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/invalid_versioner_option.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class InvalidVersionerOption < Base
def initialize(strategy)
super(message: compose_message('invalid_versioner_option', strategy: strategy))
super(message: compose_message(:invalid_versioner_option, strategy: strategy))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/invalid_with_option_for_represent.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class InvalidWithOptionForRepresent < Base
def initialize
super(message: compose_message('invalid_with_option_for_represent'))
super(message: compose_message(:invalid_with_option_for_represent))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/missing_group_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class MissingGroupTypeError < Base
def initialize
super(message: compose_message('missing_group_type'))
super(message: compose_message(:missing_group_type))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/missing_mime_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class MissingMimeType < Base
def initialize(new_format)
super(message: compose_message('missing_mime_type', new_format: new_format))
super(message: compose_message(:missing_mime_type, new_format: new_format))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/missing_option.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class MissingOption < Base
def initialize(option)
super(message: compose_message('missing_option', option: option))
super(message: compose_message(:missing_option, option: option))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/missing_vendor_option.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class MissingVendorOption < Base
def initialize
super(message: compose_message('missing_vendor_option'))
super(message: compose_message(:missing_vendor_option))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/unknown_options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class UnknownOptions < Base
def initialize(options)
super(message: compose_message('unknown_options', options: options))
super(message: compose_message(:unknown_options, options: options))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/unknown_parameter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class UnknownParameter < Base
def initialize(param)
super(message: compose_message('unknown_parameter', param: param))
super(message: compose_message(:unknown_parameter, param: param))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/unknown_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class UnknownValidator < Base
def initialize(validator_type)
super(message: compose_message('unknown_validator', validator_type: validator_type))
super(message: compose_message(:unknown_validator, validator_type: validator_type))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/unsupported_group_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class UnsupportedGroupTypeError < Base
def initialize
super(message: compose_message('unsupported_group_type'))
super(message: compose_message(:unsupported_group_type))
end
end
end
Expand Down
3 changes: 1 addition & 2 deletions lib/grape/exceptions/validation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ class Validation < Grape::Exceptions::Base
def initialize(args = {})
fail 'Params are missing:' unless args.key? :params
@params = args[:params]
@message_key = args[:message_key]
args[:message] = translate_message(args[:message_key]) if args.key? :message_key
args[:message] = translate_message(args[:message]) if args.key? :message
super
end

Expand Down
25 changes: 21 additions & 4 deletions lib/grape/validations/params_scope.rb
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ def validates(attrs, validations)
default = validations[:default]
doc_attrs[:default] = default if validations.key?(:default)

values = validations[:values]
values = (options_key?(:values, :value, validations)) ? validations[:values][:value] : validations[:values]
doc_attrs[:values] = values if values

coerce_type = guess_coerce_type(coerce_type, values)
Expand All @@ -224,6 +224,7 @@ def validates(attrs, validations)
if validations.key?(:presence) && validations[:presence]
validate('presence', validations[:presence], attrs, doc_attrs)
validations.delete(:presence)
validations.delete(:message) if validations.key?(:message)
end

# Before we run the rest of the validators, let's handle
Expand Down Expand Up @@ -254,8 +255,12 @@ def infer_coercion(validations)
fail ArgumentError, ':type may not be supplied with :types'
end

validations[:coerce] = validations[:type] if validations.key?(:type)
validations[:coerce] = validations.delete(:types) if validations.key?(:types)
validations[:coerce] = (options_key?(:type, :value, validations) ? validations[:type][:value] : validations[:type]) if validations.key?(:type)
validations[:coerce_message] = (options_key?(:type, :message, validations) ? validations[:type][:message] : nil) if validations.key?(:type)
validations[:coerce] = (options_key?(:types, :value, validations) ? validations[:types][:value] : validations[:types]) if validations.key?(:types)
validations[:coerce_message] = (options_key?(:types, :message, validations) ? validations[:types][:message] : nil) if validations.key?(:types)

validations.delete(:types) if validations.key?(:types)

coerce_type = validations[:coerce]

Expand Down Expand Up @@ -300,11 +305,13 @@ def coerce_type(validations, attrs, doc_attrs)

coerce_options = {
type: validations[:coerce],
method: validations[:coerce_with]
method: validations[:coerce_with],
message: validations[:coerce_message]
}
validate('coerce', coerce_options, attrs, doc_attrs)
validations.delete(:coerce_with)
validations.delete(:coerce)
validations.delete(:coerce_message)
end

def guess_coerce_type(coerce_type, values)
Expand Down Expand Up @@ -342,6 +349,16 @@ def validate_value_coercion(coerce_type, values)
return unless value_types.any? { |v| !v.is_a?(coerce_type) }
fail Grape::Exceptions::IncompatibleOptionValues.new(:type, coerce_type, :values, values)
end

def extract_message_option(attrs)
return nil unless attrs.is_a?(Array)
opts = attrs.last.is_a?(Hash) ? attrs.pop : {}
(opts.key?(:message) && !opts[:message].nil?) ? opts.delete(:message) : nil
end

def options_key?(type, key, validations)
validations[type].respond_to?(:key?) && validations[type].key?(key) && !validations[type][key].nil?
end
end
end
end
2 changes: 1 addition & 1 deletion lib/grape/validations/validators/all_or_none.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class AllOrNoneOfValidator < MultipleParamsBase
def validate!(params)
super
if scope_requires_params && only_subset_present
fail Grape::Exceptions::Validation, params: all_keys, message_key: :all_or_none
fail Grape::Exceptions::Validation, params: all_keys, message: message(:all_or_none)
end
params
end
Expand Down
4 changes: 2 additions & 2 deletions lib/grape/validations/validators/allow_blank.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module Grape
module Validations
class AllowBlankValidator < Base
def validate_param!(attr_name, params)
return if @option || !params.is_a?(Hash)
return if (options_key?(:value) ? @option[:value] : @option) || !params.is_a?(Hash)

value = params[attr_name]
value = value.strip if value.respond_to?(:strip)
Expand All @@ -23,7 +23,7 @@ def validate_param!(attr_name, params)

return if value == false || value.present?

fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :blank
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:blank)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/validations/validators/at_least_one_of.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class AtLeastOneOfValidator < MultipleParamsBase
def validate!(params)
super
if scope_requires_params && no_exclusive_params_are_present
fail Grape::Exceptions::Validation, params: all_keys, message_key: :at_least_one
fail Grape::Exceptions::Validation, params: all_keys, message: message(:at_least_one)
end
params
end
Expand Down
10 changes: 10 additions & 0 deletions lib/grape/validations/validators/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ def self.inherited(klass)
short_name = convert_to_short_name(klass)
Validations.register_validator(short_name, klass)
end

def message(default_key = nil)
options = instance_variable_get(:@option)
options_key?(:message) ? options[:message] : default_key
end

def options_key?(key, options = nil)
options = instance_variable_get(:@option) if options.nil?
options.respond_to?(:key?) && options.key?(key) && !options[key].nil?
end
end
end
end
6 changes: 3 additions & 3 deletions lib/grape/validations/validators/coerce.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ def initialize(*_args)
end

def validate_param!(attr_name, params)
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :coerce unless params.is_a? Hash
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:coerce) unless params.is_a? Hash
new_value = coerce_value(params[attr_name])
if valid_type?(new_value)
params[attr_name] = new_value
else
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :coerce
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:coerce)
end
end

Expand Down Expand Up @@ -60,7 +60,7 @@ def coerce_value(val)
#
# @return [Class]
def type
@option[:type]
@option[:type].is_a?(Hash) ? @option[:type][:value] : @option[:type]
end
end
end
Expand Down
11 changes: 10 additions & 1 deletion lib/grape/validations/validators/exactly_one_of.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,20 @@ class ExactlyOneOfValidator < MutualExclusionValidator
def validate!(params)
super
if scope_requires_params && none_of_restricted_params_is_present
fail Grape::Exceptions::Validation, params: all_keys, message_key: :exactly_one
fail Grape::Exceptions::Validation, params: all_keys, message: message(:exactly_one)
end
params
end

def message(default_key = nil)
options = instance_variable_get(:@option)
if options_key?(:message)
(options_key?(default_key, options[:message]) ? options[:message][default_key] : options[:message])
else
default_key
end
end

private

def none_of_restricted_params_is_present
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/validations/validators/mutual_exclusion.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class MutualExclusionValidator < MultipleParamsBase
def validate!(params)
super
if two_or_more_exclusive_params_are_present
fail Grape::Exceptions::Validation, params: processing_keys_in_common, message_key: :mutual_exclusion
fail Grape::Exceptions::Validation, params: processing_keys_in_common, message: message(:mutual_exclusion)
end
params
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/validations/validators/presence.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def validate!(params)

def validate_param!(attr_name, params)
return if params.respond_to?(:key?) && params.key?(attr_name)
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :presence
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:presence)
end
end
end
Expand Down
Loading

0 comments on commit 1d46522

Please sign in to comment.