はじめに
基本的には、以下のスライドを参考にまとめたものになります。
https://speakerdeck.com/jnchito/number-rubykansai-2018-01-13
例外とは
例外とは、それ以上プログラムを実行できない状況のこと。また、Wikipediaによると例外(exception)はシステム担当者が問題解決を行う必要がある。と書かれている。
例外処理とは
発生した例外を適切に処理すること。
Wikipediaによると、
「システムの設計で想定されておらず、ユーザー操作によって解決できない問題に対処するための処理である。例外処理の結果として問題が解決されないとシステム障害になる。」
と書かれている。
エラーとは
Wikipediaによると、
「ユーザーが解決すべき問題はエラー(error)と呼ぶ」
と書かれている。
例外処理の使い所
これまでに例外とエラーの定義についてみてきた。これらの定義に則ると例外を扱う必要があるのは次のような場合である。
問題がにユーザーによって解決できない
具体的には以下のようなケースが考えられる。
- プログラムのミス
- ネットワークエラー
- OSのエラー
- データベースのダウン
などである。
逆に例外処理として扱うべきではないパターンは次のような場合である。
問題がユーザーによって解決できる
具体的には以下のようなケースが考えられる。
- 入力フォームによる入力ミス
- 認証、認可が必要な操作へのアクセス
ちなみに例外とエラーについてWikipediaでは業務エラー、システムエラーと区別している。また紹介したスライドでは業務エラーをビジネス例外、システムエラーを技術的例外と呼んでいる。
transactionでの例外、エラーの扱い
まず、前提知識としてRailsにおけるtransactionの動作について説明する。
Railsでは以下のようにtransactionを定義する。
ActiveRecord::Base.transaction do
record.save!
end
このブロック内で何かしらの例外が発生した場合にrollbackが行われるようRailsが処理を行なっている。
では、以下のコードではどうなるか?
ActiveRecord::Base.transaction do
record.save!
rescue Exception
false
end
ここでは、自ら例外を補足しています。この場合Railsは自動でrollbackしてくれない!
なので自分でrollbackさせてあげる処理を書く必要がある。
ActiveRecord::Base.transaction do
record.save!
rescue Exception
raise ActiveRecord::Rollback
end
詳しくはこちらの記事を参照しました。
トランザクション中にrescueするとロールバックしないので注意!
以上を踏まえるとRailsにおけるtransaction処理は以下のようにするのがベストプラクティスらしい
def save
success = true
ActiveRecord::Base.transaction do
# save
success &= record1.save
success &= record2.save
unless success
raise ActiveRecord::Rollback
ned
end
success
end
ここでは、save
メソッドを用いてる。save!
にしない理由としては、save!
は、バリデーションに失敗した場合でも例外を発生させる。しかし、バリデーションに失敗する原因はほぼユーザーに起因するものである。したがって、業務エラーに区分されるため例外として扱わずただのエラーとして処理を行う。
最後に
もっと一般的な例外の扱い方については以下の記事を参考にすると良い。
https://qiita.com/jnchito/items/3ef95ea144ed15df3637