I normalize the email addresses in a before_validation
, e.g.:
class User
before_validation do
self.email = normalized_email
end
def normalized_email
User.normalize_email(email)
end
end
But then I have to do User.find_by(email: User.normalize_email(params[:email]))
everywhere for instance, and it bit me today when trying to find an user I forgot to normalize the email address, so I would like to have it done automatically.
Ideally I would not overwrite find_by
and it would work for all the methods, like where
for instance.
How can I do it?
CodePudding user response:
I would just define a special method for this use-case:
class User
def self.by_email(email)
find_by(email: User.normalize_email(email))
end
before_validation do
self.email = normalized_email
end
def normalized_email
User.normalize_email(email)
end
end
Which can then be used like this:
User.by_email(params[:email])
Please note that I explicitly didn't suggest using a scope
for this, because by convention scopes should be chainable and therefore should return ActiveRecord::Relations
what doesn't work well when you only want to return a single record.
CodePudding user response:
You can override find_by
in User to ensure this happens consistently.
class << self
def find_by(*args)
# find_by can be called without a Hash, make sure.
attrs = args.first
super unless attrs.is_a?(Hash)
# Cover both calling styles, find_by(email: ...) and find_by("email": ...)
[:email, "email"].each do |key|
# Be careful to not add email: nil to the query.
attrs[key] = normalized_email(attrs[key]) if attrs.key?(key)
end
super
end
end
This also covers find_by!
, find_or_*
, and create_or_find_by*
methods. It doesn't cover where
nor raw SQL.
You could override where
, but there's some odd caching happening which makes that troublesome.