こんにちは、河野です。
chatty_error というgemを作ってみました。
chatty_error
独自の例外クラスのエラーメッセージを、ロケールファイルから設定できるようになるライブラリです。
エラークラスをインスタンス化する時、エラーメッセージを設定するのが面倒だ!という問題を解決します。
chattyという単語に、
chatty: 【形】話し好きな、〔文章や話し方が〕くだけた
という意味があるようなので、chatty_error
と名付けました。
作った背景
ある案件で、独自の例外クラスを作ったのですが、例外をraise するときに以下のようにしていました。
1 2 |
# とんでもないことが起きた raise MyError.new(I18n.t('my_error.tondemonai_kotoga_okita')) |
他のところでは、違うメッセージを
1 2 |
# 不正なアクセスかもしれない raise MyError.new(I18n.t('my_error.huseina_akusesu_kamo_sirenai')) |
また別のところでは、
1 2 |
# 処理の手順がおかしい raise MyError.new(I18n.t('my_error.tezyunga_okasii')) |
という感じで、同じエラーでもメッセージを変えることがありました。
理想を言えば細かく例外クラスを作った方が良さそうなのですが、とりあえずこんな感じで対応していたのです。
で、色んなところで、MyErrorをraiseする度に、I18nを呼び出さないといけません。
これは何かモヤモヤする~ということで、chatty_error の登場です。
使い方
Gemのインストール
まずはgemを使えるようにします。bundlerの場合です。
1 2 |
# Gemfile gem 'chatty_error' |
bundleを実行します。
1 |
$ bundle |
これで、ChattyError
というモジュールが使用できるようになります。
原因を設定
さて、chatty_error では原因に対してメッセージを設定するという方針を採っています。なので、独自の例外クラスでは原因を設定する必要があります。caused_by
を使用します。
MyErrorの例だと、以下のようになります。
1 2 3 4 5 6 |
# my_error.rb class MyError < StandardError include ChattyError caused_by :terrible_thing_happend, :unauthorized_access, :wrong_procedure end |
「とんでもないことが起きた」「不正なアクセス」「間違った手順」という例外が起きる原因を定義しています。
メッセージを設定
次に、原因に対応するエラーメッセージを設定します。
1 2 3 4 5 6 7 |
# ja.yml ja: chatty_errors: my_error: terrible_thing_happend: "とんでもないことが起きた!!!" unauthorized_access: "不正なアクセスだ!!!" wrong_procedure: "順番間違ってる" |
例外の呼び出し方
raiseの時には、
1 2 3 |
raise MyError.terrible_thing_happend raise MyError.unauthorized_access raise MyError.wrong_procedure |
という感じで、簡単に呼び出せます。
MyErrorのインスタンス化とともに、エラーメッセージも設定されます。ついでに、cause
に原因に相当するシンボルが設定されます。
1 2 3 |
error = MyError.unauthorized_access error.message # => 不正なアクセスだ!!! error.cause # => :unauthorized_access |
ロケールファイルの書き方
ロケールファイルに設定するキーは、「chatty_errors.クラス階層.原因」になります。
仮に以下のような定義になっていたら、
1 2 3 4 5 6 7 8 9 10 11 12 |
# deep.rb module A module B module C class Deep < StandardError include ChattyError caused_by :deeper end end end end |
ロケールファイルは
1 2 3 4 5 6 7 8 |
# ja.yml ja: chatty_errors: a: b: c: deep: deeper: "階層深い…" |
となります。
デフォルト設定
ロケールファイル上のデフォルトスコープと、キーが一致しなかった場合のデフォルトメッセージを設定することができます。
1 2 3 4 5 6 7 8 9 10 11 |
# my_error.rb class MyError < StandardError include ChattyError configure do |c| c.default_scope = 'techscore' c.default_message = 'エラーだよ' end caused_by :terrible_thing_happend, :unauthorized_access, :wrong_procedure end |
デフォルトスコープを変更しているので、ロケールファイルは以下のようになります。
1 2 3 4 5 6 7 |
# ja.yml ja: techscore: my_error: terrible_thing_happend: "とんでもないことが起きた!!!" unauthorized_access: "不正なアクセスだ!!!" wrong_procedure: "順番間違ってる" |
エラーメッセージの決定
エラーメッセージの決定は以下優先順位になります。
1. デフォルトスコープ、クラス階層、メソッド名から決定されるキーと対応するロケールファイル上のメッセージ
2. 'デフォルトスコープ.default_message' というキーと対応するロケールファイル上のメッセージ
3. configureで設定した、default_message に設定されているメッセージ
ちょっとした活用例
Railsの場合ですが、ApplicationControllerの中で rescue_fromを使用しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class ApplicationController < ActionController::Base rescue_from MyError, :with => :my_error_handle def my_error_handle(exception) case exception.cause when :unauthorized_access logger.warn([exception.message].join("\n")) else logger.error([exception.message].join("\n")) end render 'errors/500', :status => '500 Internal Server Error', :layout => 'errors/application' end end |
MyErrorのcauseによって、ログレベルを変えるという処理です。他にも色々と使いどころはあると思います。
リンク
以上です。