开发者

How do I validate that two values do not equal each other in a Rails model?

I have a User model, wh开发者_开发知识库ich has an email and a password field. For security, these may not be equal to each other. How can I define this in my model?


Create custom validataion:

validate :check_email_and_password

def check_email_and_password
  errors.add(:password, "can't be the same as email") if email == password
end

But keep in mind that storing password as a plain text is bad idea. You should store it hashed. Try some authentication plugin like authlogic or Restful authentication.


New way:

validates :password, exclusion: { in: lambda{ |user| [user.email] } }

or:

validates :password, exclusion: { in: ->(user) { [user.email] } }


You can use a custom validation method to check this.

class User < ActiveRecord::Base
  # ...

  def validate
    if (self.email == self.password)
      errors.add(:password, "password cannot equal email")
      errors.add(:email, "email cannot equal password")
    end
  end
end


It depends how Your password is stored:

class User < ActiveRecord::Base
    validate :email_and_password_validation

    def email_and_password_validation
        if self.email == self.password
            errors.add_to_base("Password must be different from email") 
        end
    end
end

This would work if Your password is stored literally, but You can perform the same thing with email (e.g. create a hashed version) and check for equality with password. E.g:

class User < ActiveRecord::Base
    validate :email_and_password_validation

    def email_and_password_validation
        if make_hash(self.email) == self.hashed_password
            errors.add_to_base("Password must be different from email") 
        end
    end
end

My example is taken from http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M002162

Your situation is quite general so You can be interested in creating custom validation method. Everything is covered here: http://guides.rubyonrails.org/active_record_validations_callbacks.html#creating-custom-validation-methods


it is much wiser to use custom validator, here is code for universal validator that can be used

class ValuesNotEqualValidator < ActiveModel::Validator
  def validate(record)
    if options[:fields].any? && options[:fields].size >= 2
      field_values = options[:fields].collect { |f| record.send(f) }
      unless field_values.size == field_values.uniq.size
        record.errors[:base] <<
            (options[:msg].blank? ? "fields: #{options[:fields].join(", ")} - should not be equal" :
                options[:msg])
      end
    else
      raise "#{self.class.name} require at least two fields as options [e.g. fields: [:giver_id, :receiver_id]"
    end
  end
end

and then use it like:

class User < ActiveRecord::Base
  # ...
  validates_with ValuesNotEqualValidator, fields: [:email, :password], msg: "This Person is evil"
end


more fun:

  validates :password, exclusion: { in: ->(person) { [person.email] }, message: "cannot use protected password" }

which might be even better in this case, since you could check for other forbidden values with something like

  validates :password, exclusion: { in: ->(person) { [person.email, person.first_name, person.last_name, person.phone_number, person.department_name] }, message: "cannot use protected password" }


Ruby on Rails 7.0 added support for validates_comparison_of validations like this

validates :password, comparison: { other_than: :email }


all you need is to create validation rule in your model for example

class User < ActiveRecord::Base
  def validate_on_create
    if email == password
      errors.add("password", "email and password can't be the same")
    end
  end
end


If you want to support multiple languages, you have to come up with another solution, which translates the error messages and the attribute names. So I created a new each validator for that.

validators/values_not_equal_validator.rb:
class ValuesNotEqualValidator < ActiveModel::EachValidator
  def validate(record)
    @past = Hash.new
    super
  end

  def validate_each(record, attribute, value)
    @past.each do |k, v|
      if v == value
        record.errors.add(attribute, I18n.t('errors.messages.should_not_be_equal_to') + " " + record.class.human_attribute_name(k))
      end
    end
    @past[attribute] = value
  end
end

I call it in the model like this:

class User < ActiveRecord::Base
  validates :forename, :surname, values_not_equal: true
end

And I translate it the messages like this:

de:
  activerecord:
    attributes:
      user:
        forename: 'Vorname'
        surname: 'Nachname'
  errors:
    messages:
      should_not_be_equal_to: 'darf nicht gleich sein wie'


By using custom validations we can do this operation

validate :validate_address

def validate_address
errors.add(:permenent_address, "can't be the same as present_address") if self.present_address== self.permenent_address end

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜