A simple base class that can be used along with ActiveModel::Validations::ClassMethods.validates_with
class Person include ActiveModel::Validations validates_with MyValidator end class MyValidator < ActiveModel::Validator def validate(record) if some_complex_logic record.errors.add(:base, "This record is invalid") end end private def some_complex_logic # ... end end
Any class that inherits from ActiveModel::Validator
must implement a method called validate
which accepts a record
.
class Person include ActiveModel::Validations validates_with MyValidator end class MyValidator < ActiveModel::Validator def validate(record) record # => The person instance being validated options # => Any non-standard options passed to validates_with end end
To cause a validation error, you must add to the record
's errors directly from within the validators message.
class MyValidator < ActiveModel::Validator def validate(record) record.errors.add :base, "This is some custom error message" record.errors.add :first_name, "This is some complex validation" # etc... end end
To add behavior to the initialize method, use the following signature:
class MyValidator < ActiveModel::Validator def initialize(options) super @my_custom_field = options[:field_name] || :first_name end end
Note that the validator is initialized only once for the whole application life cycle, and not on each validation run.
The easiest way to add custom validators for validating individual attributes is with the convenient ActiveModel::EachValidator
.
class TitleValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) record.errors.add attribute, 'must be Mr., Mrs., or Dr.' unless %w(Mr. Mrs. Dr.).include?(value) end end
This can now be used in combination with the validates
method (see ActiveModel::Validations::ClassMethods.validates
for more on this).
class Person include ActiveModel::Validations attr_accessor :title validates :title, presence: true, title: true end
It can be useful to access the class that is using that validator when there are prerequisites such as an attr_accessor
being present. This class is accessible via options[:class]
in the constructor. To set up your validator override the constructor.
class MyValidator < ActiveModel::Validator def initialize(options={}) super options[:class].attr_accessor :custom_attribute end end
# File activemodel/lib/active_model/validator.rb, line 103 def self.kind @kind ||= name.split("::").last.underscore.chomp("_validator").to_sym unless anonymous? end
Returns the kind of the validator.
PresenceValidator.kind # => :presence AcceptanceValidator.kind # => :acceptance
# File activemodel/lib/active_model/validator.rb, line 108 def initialize(options = {}) @options = options.except(:class).freeze end
Accepts options that will be made available through the options
reader.
# File activemodel/lib/active_model/validator.rb, line 116 def kind self.class.kind end
Returns the kind for this validator.
PresenceValidator.new(attributes: [:username]).kind # => :presence AcceptanceValidator.new(attributes: [:terms]).kind # => :acceptance
# File activemodel/lib/active_model/validator.rb, line 122 def validate(record) raise NotImplementedError, "Subclasses must implement a validate(record) method." end
Override this method in subclasses with validation logic, adding errors to the records errors
array where necessary.
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.