You have a slick, exclusive, invite-only Web app for sharing Tor URLs, with an Android client and
specialty hardware. You use
validatesemailformat_ofin
the Invitation
model, but still something slips through and your
Hoptoad errors pile up, showing your user the
beautifully-designed 500 page instead of an error explanation.
There are two types of exceptions that ActionMailer
will raise when you
attempt to deliver an email: user input problems and server problems.
User input problems are those such as incorrect or invalid email addresses; the
exceptions raised are Net::SMTPFatalError
and Net::SMTPSyntaxError
. These
are issues that the user can fix and as such the error message should indicate
that everything’s fine, nothing is ruined.
Server problems could be anything from a non-existent server to an
authentication issue; the exceptions raised are: TimeoutError
, IOError
,
Net::SMTPUnknownError
, Net::SMTPServerBusy
, and
Net::SMTPAuthenticationError
. These issues are outside the power of the user
and should indicate that we screwed up.
So in config/initializers/errors.rb
:
SMTP_SERVER_ERRORS = [
IOError,
Net::SMTPAuthenticationError,
Net::SMTPServerBusy,
Net::SMTPUnknownError,
TimeoutError,
]
SMTP_CLIENT_ERRORS = [Net::SMTPFatalError, Net::SMTPSyntaxError]
SMTP_ERRORS = SMTP_SERVER_ERRORS.concat(SMTP_CLIENT_ERRORS)
SMTP_CLIENT_ERROR_FLASH = 'The email address supplied is invalid. Please
check for spelling mistakes.'
SMTP_SERVER_ERROR_FLASH = 'We encountered an internal issue while attempting
to deliver this email. Please try again in a few minutes.'
We can test it with invitations_controller_test.rb
:
class InvitationsController; def rescue_action(e) raise e end; end
class InvitationsControllerTest < Test::Unit::TestCase
SMTP_CLIENT_ERRORS.each do |exn|
should "handle #{exn}" do
InvitationsMailer.expects(:deliver_invitation).raises(exn)
post :create, :invitation => {:email => 'invalid email'}
assert_match /#{SMTP_CLIENT_ERROR_FLASH}/i, @response.flash[:warning]
assert_template 'new'
end
end
SMTP_SERVER_ERRORS.each do |exn|
should "handle #{exn}" do
InvitationsMailer.expects(:deliver_invitation).raises(exn)
post :create, :invitation => {:email => 'invalid email'}
assert_match /#{SMTP_SERVER_ERROR_FLASH}/i, @response.flash[:warning]
assert_template 'new'
end
end
end
And in invitations_controller.rb
:
class InvitationsController < ApplicationController
def create
@invitation.new(params[:invitation])
if @invitation.save
redirect_to root_url
else
render :action => 'new'
end
rescue *SMTP_CLIENT_ERRORS
flash[:warning] = SMTP_CLIENT_ERROR_FLASH
render :action => 'new'
rescue *SMTP_SERVER_ERRORS => error
notify_hoptoad error
flash[:warning] = SMTP_SERVER_ERROR_FLASH
render :action => 'new'
end
end
If you use Suspenders you’ll be
pleased to find that we’ve included
config/initializers/errors.rb
for you pre-populated with both SMTP and HTTP
exceptions.