vvohra87
vvohra87

Reputation: 5674

DRY way to write many custom validators in rails

In my applications, there are multiple custom validators that I keep in app/validators and then call in multiple models.

Mostly I do this for regex based validations, things like email, mobiles, etc basically custom data strings that I need to be in a particular format.

For example:

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

class IpValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    unless value =~ /^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/
      record.errors[attribute] << (options[:message] || "is not a valid IP format")
    end
  end
end

The downside of this method is that I have a lot of extra files with pretty much the same code which can get annoying in larger applications.

Is there a better way (more DRY) to define regex based validations such that they are reusable across models?

Upvotes: 1

Views: 385

Answers (1)

Louis Kottmann
Louis Kottmann

Reputation: 16628

You can make a base class for regexp validation:

class RegexValidator < ActiveModel::EachValidator
  def regex_validate_each(regex, err_msg, record, attribute, value)
    unless value =~ regex
      record.errors[attribute] << (options[:message] || err_msg)
    end
  end
end

Then subclass it like:

class EmailValidator < RegexValidator
  def validate_each(record, attribute, value)
    regex_validate_each(/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, "is not a valid IP format", record, attribute, value)
  end
end

Upvotes: 2

Related Questions