Validating Emails in Ruby on Rails

It’s time to validate e-mail addresses and you’re sitting in a Ruby on Rails application. Fortunately, there are a few methods to tackle such a task, and a combination of them can yield a pretty nice solution.

The first idea is to use some lengthy regular expressions. But why enumerate/describe in regular expressions what we are looking for when TMail has it built in… Using TMail, it is possible to let our Ruby Net SMTP wrapper class parse the email address and decide if it is correct or not.

The second task is to make up for some of TMail’s odd shortcomings: the fact that the text “bob” passes as valid for TMail is alarming, but throwing in some simple regular expression to get past this provides a pretty solid solution. (For the curious, “bob” is a valid e-mail to TMail because you could be sending messages to the local domain.)

First, here is our regular expression for a basic e-mail address…

/^([^@\s'"]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i

Next, we create a TMail object with our e-mail address…

tmail = TMail::Address.parse(address.to_s) rescue nil

I am rescuing nil here. You can rescue whatever error message you want, but for example sake I am not so concerned if the TMail fails to parse the e-mail address. You can pass in a multitude of e-mail formats and TMail will do its best to match the RFC standard for e-mail addresses.

You now have access to a TMail object with a flurry of options (TMail & documentation). Let’s proceed.

My simply method calls TMail and then follows it with the regular expression match to ensure this e-mail address in question is ready to be used on the web. Here is my final result to a pretty safe-proof (so far, tested on a rather large web site) e-mail handler. This method will return the TMail object.

def validate_email e-mail
  tmail = TMail::Address.parse(address.to_s) rescue nil
  tmail if tmail.address =~ /^([^@\s'"]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
end

And for those of you who want the extra step, I have attached a helper method that takes a TMail object and gives you back the e-mail address in string format fully qualified.

def formatted_email tmail
  if !email.blank?
    friendly_name = (tmail.name.blank? ? "" : tmail.name.blank?).tr('"',"'")
    quote = '"' if friendly_name =~ /[<,@;]/
    "#{quote}#{friendly_name}#{quote} <#{address}>"
  end
end

As always, test this code and do not trust it blindly. I could have fat fingered something…

Google Mail Conversation Threading, and how to prevent

Tired of Google mail taking your e-mail and matching it to a gmail conversation incorrectly? Google mail has many cool features, one of which is the e-mail conversation thread view. This view shows e-mails matched based on subject, time, and correspondence grouped together to see a chronological conversation. While this is a neat feature that has made Gmail unique for so long, it can become cumbersome to companies and web developers as they try to ship out monthly statements, e-mail notices, or any other e-mail that for some reason has the same subject every time it is sent out.

The question I asked was: Is it sufficient to change the subject text every time I send out an e-mail?

The answer was no. The reason(s) were simple: the e-mail may need the same subject and/or you may not want to send your customer an e-mail with a unique character sequence in it that the user can see and potentially become confused about.

The solution: Follow suit with another cool feature Google mail has made apparent, the use of virtual inboxing. Add a short web-friendly unique code to your e-mail address username, appending with a + sign.

ephekt@gmail.com becomes ephekt+nM2eY@gmail.com

The key to this is that you should have a friendly name on your e-mail so that the recipient does not see the difference in e-mail addresses. Thus, with slight modification we now arrive at:

Mike R <ephekt@gmail.com> becomes Mike R <ephekt+nM2eY@gmail.com>

And to the end user all is sane. There are a few ways to generate unique tokens… In a Ruby on Rails project I used the built-in base 64 encoder, passing in a random number up to 3 characters long so that my generated code would not only be highly unique but relatively short.

def friendly_email_token
  ActiveSupport::Base64.encode64(rand(0x10000).to_s).tr("/+","_.").gsub(/=*\n/,"")
end

Just throw the above method into one of your classes and then you can define a method that appends this friendly_email_token to your e-mail address.

def randomize_address email
  unless email.blank?
    username, domain = email.split("@", 2)
    "#{username}+{friendly_email_token}@#{domain}"
  end
end

And voila! We’re good to go. Call the randomize_address method with your e-mail (with or without a friendly name) and it will do the work.