4

Is there a way to call standard Rails validators from within a custom validator?

I have a combination of OAuth/email signup/sign in and I want to be able to call certain validators on each method of authentication. For instance, I want to be able to call validates_uniqueness_of :email if the user signs up through email and then call a single validator, for instance validates_with UserValidator.

If there isn't a way to do this I'm just going to use state tracking and a series of :if validations.

3 Answers 3

1

I believe there's no way to call other validators from custom one, this also may possibly cause circular dependency which is dangerous.

You have to go with conditional validations, but keep in mind that you can scope them like this (taken from Rails Guides)

with_options if: :is_admin? do |admin|
  admin.validates :password, length: { minimum: 10 }
  admin.validates :email, presence: true
end
Sign up to request clarification or add additional context in comments.

Comments

1

If your goal is to call some combination of custom and standard rails validators you can do that with the validates method provided by ActiveModel::Validations

For example, you've created a custom Email Validator:

class EmailValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    record.errors.add attribute, (options[:message] || "is not an email") unless
      value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
  end
end

And you want to include it in your Person class. You can do like so:

class Person < ApplicationRecord
  attr_accessor :email

  validates :email, presence: true, email: true
end

which will call your custom validator, and the PresenceValidator. All examples here are taken from the docs ActiveModel::Validations::Validates

Comments

1

I'm not sure if this a recent change in Rails but in 6.1 it is possible to call the standard validators from a custom validator:

class VintageYearValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    ActiveModel::Validations::NumericalityValidator.new({
      attributes: attributes,
      only_integer: true,
      greater_thank_or_equal_to: 1900,
      less_than_or_equal_to: 2100
    }).validate_each(record, attribute, value)

    # Your custom validation
    errors.add() unless bla?

  end
end

Those standard Validators are not really documented (https://apidock.com/rails/v6.1.3.1/ActiveModel/Validations/NumericalityValidator) so use at your own risk. But there doesn't seem to be a risk of circular dependency. Both your custom validator and the standard Validator inherit from EachValidator.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.