Handing Undeliverable Email Address in a Rails app

User can fat finger their email address during signup. We will not be able to send any emails for password reset, purchase confirmation etc. In this case, the system needs to handle the situation gracefully. Create a custom exception class:

class UndeliverableEmailError < StandardError
  attr_reader :object

  def initialize(object)
    @object = object
  end
end

Use this custom exception class when the system encounters a situation where it cannot proceed further without manual intervention. In this case, we need to update user record with a deliverable email address. In my case, the background job processor uses this custom exception.

class NewCustomerFulfillmentJob < ApplicationJob
  queue_as :default

  def perform(user, password)
    if user.has_deliverable_email?
      PurchaseNotifierMailer.send_purchase_confirmation_email(user, password).deliver_now
    else
      exception = UndeliverableEmailError.new(user)
      message = "User #{user.email} is not deliverable. User id: #{user.id}"
      ExceptionNotifier.notify_exception(exception,
                                         env: request.env,
                                         data: {:message => message})
    end
  end
end

The exception message is constructed to provide the user id as well as the email address to aid in troubleshooting the problem. The users that are already verified by never-bounce-api API and have invalid email can be displayed in the admin dashboard. The user model has two boolean attributes, has_valid_email and deliverability_verified. The deliverability_verified attribute is set to true when the never-bounce-api call returns true. Exception notification gem is used to send the exception email.

def has_deliverable_email?
  return true if has_valid_email? && deliverability_verified?

  deliverability = EmailAddress.deliverable?(email)
  self.deliverability_verified = true
  self.has_valid_email = deliverability
  save!
  deliverability_verified? && has_valid_email?
end

If you were not sending an email and had to handle it in the code, you can do:

begin
  raise UndeliverableEmailError.new(user), "User email cannot be used to send email"
rescue UndeliverableEmailError => e
  puts e.message 
  puts e.object 
end

In this article, we saw how the system can gracefully handle a case when it cannot fulfill its job without proper data.


Related Articles