Railsでの例外処理(エラー処理)
エラーをrescueする場合の共通処理をメソッド化する
class ErrorUtility
def self.log_and_notify(e)
Rails.logger.error e.class
Rails.logger.error e.message
Rails.logger.error e.backtrace.join("\n")
Bugsnag.notify e
end
end
def self.send_daily_summary_to_all_users
User.all.each do |user|
begin
UserMail.daily_summary(user).deliver
rescue => e
ErrorUtility.log_and_notify e
end
end
end
APIから返ってきたエラーを自分でシステムエラーに変換する
API処理はエラーが頻発する可能性があるので、エラー処理を必ず書こう!
# 外部APIを利用して課金を実行する
def execute_charge!
response = PaymentApi.charge(self)
error_code = response['ErrCode']
# エラーコードが"0"以外であれば何らかのエラーが発生している
if error_code != '0'
# 課金に失敗したのでシステムエラーを発生させる
# エラーには必ず調査やデバッグに役立つ情報を含める
raise "Charge failed. ErrCode: #{error_code} / ErrMessage: #{response['ErrMessage']}"
end
response['TranID']
end
エラー処理も必ずテストする
def self.send_daily_summary_to_all_users
User.all.each do |user|
begin
UserMail.daily_summary(user).deliver
rescue => e
# "notify"をtypoしているために新たなエラーが発生し、
# 元のエラー内容が失われる!!
ErrorUtility.log_and_notfy e
end
end
end
describe User do
describe '::send_daily_summary_to_all_users' do
before do
User.create!(name: 'Alice', email: 'alice@example.com')
end
it 'エラー処理が正しく動作すること' do
# daily_summaryを呼び出したときにわざとエラーを発生させる
allow(UserMail).to receive(:daily_summary).and_raise('For test')
# エラーの共通処理が呼び出されることを検証する
expect(ErrorUtility).to receive(:log_and_notify).once
User.send_daily_summary_to_all_users
end
end
end
特定の種類のエラーだけを補足する方法
エラーを見境なく全てキャッチするアンチパターン
# NG!!
def search_tweets(keyword)
twitter_client.search(keyword)
rescue
# レートリミットエラー以外のエラー(たとえば認証エラー)が
# 起きた場合も同じように捕捉されてしまう
['レートリミットの上限に達しました。']
end
特定のエラーのみ捕捉する
def search_tweets(keyword)
twitter_client.search(keyword)
rescue Twitter::Error::TooManyRequests
# レートリミットエラーだけが捕捉される
['レートリミットの上限に達しました。']
end