What's the best way to validate multiple emails and handle errors in Rails?
-
21-08-2019 - |
Question
In the current app I'm building I've got a textarea where a user will enter a comma-delimited list of email addresses.
I'm currently splitting the list into an array and then saving one by one. But if, say, I have this input...
blah@example.com, test@example, foo@example.com
... then blah@example.com will be saved, but saving test@example will fail. So I then need to remove blah@example.com from the comma-delimited string of values that I pass back to the textarea when I show the error that test@example isn't a valid email address.
Is there a better way to validate these on the server side and handle errors without getting fancy / ugly in the controller?
Thanks in Advance!
Solution
Assuming this is a model that has_many emails, and the email model uses :validate_email, you could do something like the following:
class Foo < ActiveRecord::Base
validate :must_not_have_invalid_addresses
...
def emails=(addresses)
@invalid_addresses = []
addresses.split(",").each do |address|
@invalid_addresses.push(address) unless emails.create({:address => address})
end
end
def must_not_have_invalid_addresses
errors.add_to_base("Some email addresses were invalid") unless @invalid_addresses.empty?
end
end
This provides a validation error + an array of the invalid email addresses which you can make accessible to your view if you like.
OTHER TIPS
You can put saving emails in transaction. Then if any save will fail, then all previos saves are canceled. In such case, validations can be done only on model layer.
I think it would be clear code, but for sure it isn't the fastest possible way (but using Ruby means you are not doing it in even fast way ;) )
If you have them in a variable called emails
, perhaps something like this may work:
if valid_emails?(emails)
# then have your normal logic here
if @user.save
flash[:notice] .....
end
end
private
def valid_emails?(emails)
not emails.find {|email| email =~ /[\w\.%\+\-]+@(?:[A-Z0-9\-]+\.)+(?:[A-Z]{2,}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|jobs|museum)/i }.nil?
end
EDIT: actually you may just want to use this regular expression. It was taken from the restful-authentication plugin.