Вот пользовательский валидатор , который автоматически предоставляет интерполяционную переменную %{allowed_options}
для использования в ваших сообщениях об ошибках:
class RestrictToValidator < ActiveModel::EachValidator
ErrorMessage = "An object with the method #include? or a proc or lambda is required, " <<
"and must be supplied as the :allowed_options option of the configuration hash"
def initialize(*args)
super
@allowed_options = options[:allowed_options]
end
def check_validity!
unless [:include?, :call].any?{ |method| options[:allowed_options].respond_to?(method) }
raise ArgumentError, ErrorMessage
end
end
def allowed_options(record)
@allowed_options.respond_to?(:call) ? @allowed_options.call(record) : @allowed_options
end
def allowed_options_string(record)
allowed_options = allowed_options(record)
if allowed_options.is_a?(Range)
"#{allowed_options}"
else
allowed_options.to_sentence(last_word_connector: ', or ')
end
end
def validate_each(record, attribute, value)
allowed_options = allowed_options(record)
inclusion_method = inclusion_method(allowed_options)
unless allowed_options.send(inclusion_method, value)
record.errors.add(attribute, :restrict_to,
options.except(:in).merge!(
value: value,
allowed_options: allowed_options_string(record)
)
)
end
end
private
# In Ruby 1.9 <tt>Range#include?</tt> on non-numeric ranges checks all possible values in the
# range for equality, so it may be slow for large ranges. The new <tt>Range#cover?</tt>
# uses the previous logic of comparing a value with the range endpoints.
def inclusion_method(enumerable)
enumerable.is_a?(Range) ? :cover? : :include?
end
end
Включить в ваш config / locales / en.yml:
en:
activerecord:
errors:
messages:
restrict_to: "is not one of the allowed options (%{allowed_options})"
Вы можете использовать это так:
DegreeOptions = ['High School', 'Associates', 'Bachelors', 'Masters', 'Doctorate']
validates :highest_degree, restrict_to: {allowed_options: DegreeOptions},
allow_blank: true, presence: true
# => "highest_degree is not one of the allowed options (High School, Associates, Bachelors, Masters, or Doctorate)"
Или с диапазоном:
validates :letter_grade, restrict_to: {allowed_options: 'A'..'F'}
# => "letter_grade is not one of the allowed options (A..F)"
Или с лямбда / процесс:
validates :address_state, restrict_to: {
allowed_options: ->(person){ Carmen::states(country)
}
Комментарии приветствуются!Как вы думаете, что-то подобное должно быть добавлено в ядро Rails (ActiveModel)?
Есть ли лучшее имя для этого валидатора?restrict_to_options
?restrict_to