I have a User
model that needs to validate the uniqueness of a field before it is saved, this field however, is changed before it is saved (it is hashed). How can I do this? I tried the following: Adding a validate digest at the end of the before save call, but that doesn't work.
class User < ApplicationRecord
has_secure_password
before_save :downcase_email, :downcase_name, :digest_email, :validate_email_digest
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }, presence: true
validates :name, length: { within: 2..40 }, presence: true, uniqueness: true
validates :password_digest, presence: true
self.implicit_order_column = 'created_at'
private
def validate_email_digest
# This throws an exception
validates_uniqueness_of :email, message: 'That email is already in use.'
end
def downcase_email
self.email = email.downcase
end
def downcase_name
self.name = name.downcase
end
def digest_email
self.email = Digest::SHA2.hexdigest(email).to_s
end
end
How can I validate the result of the model before_save
before they are saved to the database? In this case how can I validate that the digested email is unique after it's been digested by before_save :digest_email
?
The steps I'm looking to perform:
- Validate the emails form (ie: test it against email regexp)
- Hash it
- Then validate again that the hash is unique within the table
I would prefer to do this all with validations, and keep my controllers form having much logic.
CodePudding user response:
So turns out for what I needed exactly I need to make a validate
call and then verify the uniqueness that way. I used the following code:
class User < ApplicationRecord
has_secure_password
before_validation :downcase_name, :downcase_email
before_save :digest_email
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }, presence: true
validates :name, length: { within: 2..40 }, presence: true, uniqueness: true
validates :password, presence: true, confirmation: true
validates :password_confirmation, presence: true, on: :create
validate :verify_email_hash_uniqueness, on: :create
self.implicit_order_column = 'created_at'
private
def downcase_email
self.email = email.downcase
end
def downcase_name
self.name = name.downcase
end
def digest_email
self.email = Digest::SHA2.hexdigest(email).to_s
end
def verify_email_hash_uniqueness
email_digest = Digest::SHA2.hexdigest(email).to_s
errors.add(:base, 'Email is already in use') if User.where(email: email_digest).exists?
end
end
This will test uniqueness AFTER it tests if the email is well formed, allowing me to hash emails before putting them into the database, verifying their form and uniqueness.