そもそもRailsでカスタム例外を定義すべき機会は少ない
はじめに
今仕事でRailsアプリケーションの運用をやっているのですが、いろいろなところで例外が定義されていて「これ必要なくね…」となんとなく思ったことが多々ありました。 しかし、effective rubyには「raiseにはただの文字列ではなくカスタム例外を渡そう」という章もあり、この違いはなんだろうと思いました。 なんでそう思ったのか整理して、今後自分のプログラミングに生かして行こうと思います。まとめると以下になります。
- カスタム例外を自分でraiseしてrescueするな。戻り値で判断しろ。
よく見るコード
Resourceモデル
class Resource Error = Class.new(StandardError) def write # 何かの処理 result = false # 何かの処理の結果falseだったと仮定 raise Error, '保存時にエラーが発生しました。' end end
Resourcesコントローラ
class ResourcesController < ApplicationController def create Resource.new.write redirect_to '/', flash: { notice: '作成されました' } rescue Resource::Error => e redirect_to '/', flash: { error: e.message } end end
よく見るコードがあまり良くない理由
- 処理の成功失敗を例外で返すようにすると、使いたいところで毎回例外処理を書かないといけないといけない。
- モデル側での処理が複雑になった場合に、大きい範囲をrescueする形になる。
- railsでは習慣的に、例外ではなく戻り値で判断する。
- 詳細はjnchitoさんの記事がわかりやすいです。
汎用性も可読性も低い。一般的でもないということですね…。
どうすればいいか
Resourceモデル
class Resource def write # 何かの処理 false # 何かの処理の結果falseだったと仮定 end end
Resourcesコントローラ
class ResourcesController < ApplicationController def create if Resource.new.write redirect_to '/', flash: { notice: '作成されました' } else redirect_to '/', flash: { error: '作成されませんでした' } end end end
カスタム例外は必要なくなりましたし、表示用のメッセージがモデルに縛られなくなりました。
結局カスタム例外は必要ないの?
必要ないとは言いませんが、単体のRailsアプリケーションを書いている場合、自分でraiseしようとしている例外はそもそもシステムエラーではなく業務エラー(日常的におこりうる)であることが多いように思いました。 もしそれがシステムエラーならば、rescueして握りつぶしては検知できないですし、そうなるとrescueしないんだからカスタム例外ではなくてもいいよねという話になると思い、最終的に独自例外いらないよねということになるんじゃないかなと思います。。。
gemなど不特定多数の人が使うものに関しては積極的に定義していくべきだと思います。gemから返ってくる例外がRuntimeErrorだとつらいですしね。
まとめ
やっぱりRailsでカスタム例外を定義すべき機会は少ないし、少なくとも自分はめったにやらないかなと思いました。