はじめに
こんにちは。アメリカ在住で独学エンジニアを目指している Taira です。
Rails で rescue_from を使ってエラーハンドリングするときに、つい
e.message || I18n.t("application.errors.not_found")
と書いてしまうことはありませんか?
一見これで「e.message がなければ翻訳メッセージを出す」ように見えますが、実は思わぬ落とし穴があります。
問題となるケース
例えば以下のようなコード:
rescue_from ActiveRecord::RecordNotFound do |e|
render_error!(
code: "NOT_FOUND",
field: "base",
message: e.message || I18n.t("application.errors.not_found"),
status: :not_found
)
end
上記はオリジナルメソッドですが、要は情報を渡してjsonレスポンスを返すだけのものです。
このとき、もし e.message が 空文字 "" の場合、どうなるでしょうか?
実際には…
"" || I18n.t("application.errors.not_found")
# => ""
となり、空文字がそのまま返ってしまうのです。
つまりエラーメッセージが空っぽのレスポンスになってしまいます。
なぜ起きるのか?
Ruby の || は「nil または false なら右側を評価」する仕組みです。
空文字 "" は truthy(真)なので || の左側として成立してしまい、右側が評価されません。
解決方法:presence を使う
Rails が提供する Object#presence を使うと、この問題を簡単に回避できます。
e.message.presence || I18n.t("application.errors.not_found")
presence は以下のように動きます:
- 値が
nilや空("",[],{}など) →nilを返す - それ以外ならそのまま返す
つまり、e.message が空文字だった場合でも nil に変換されるので、期待通りフォールバックできます。
まとめ
-
e.message || ...だけだと、空文字がそのまま返ってしまう - Ruby の
||判定は「nil または false」だけ - Rails の
presenceを組み合わせることで、空文字でもちゃんとフォールバック可能
エラーメッセージを返すときは e.message.presence || I18n.t(...) を意識しておくと安心です。