この記事はSmartHR Advent Calendar 2025の8日目の記事です。
こんにちは。プレイングマネージャーとして開発をしたりしている ex_SOUL です。
最近 ActiveRecord::Base::normalizes をはじめて使ったのでその紹介をしようと思います。
ActiveRecord::Base::normalizes はRails 7.1に追加されたメソッドで、Active Recordの属性値を正規化することができます。
ActiveRecord::Base::normalizes
使い方
以下のようにして使うことができます。
class Purchase < ApplicationRecord
# emailの前後の空白を削除して小文字にしている
normalizes :email, with: ->(email) { email.strip.downcase }
# amountが空であれば0にしている
normalizes :amount, with: ->(amount) { amount.presence || 0 }, apply_to_nil: true
end
normalizes を使わない場合であれば before_saveやbefore_validation、もしくはsetterメソッド(email=(val))で実施していたであろう「ユーザーから渡されたメールアドレスの前後の空白を削除して小文字にする」処理をnormalizesで行うことができます。1
また、数値を期待する値が空文字やnilだった場合0に置換することもできます。
デフォルトでは属性値がnilの場合は発火しないため、nilガードを意識する必要もありません。
逆にnilの場合でも発火したい場合はapply_to_nil: trueを記述する必要があります。
検索
normalizesは検索時にも有効です。ユーザーが入力した値を使って検索を行う際も同様の正規化が適用されます。
# 検索条件で扱う場合も正規化される
puts Purchase.where(email: " HOGE@Example.com ").to_sql
# => SELECT "purchases".* FROM "purchases" WHERE "purchases"."email" = 'hoge@example.com'
この挙動は今までのbefore_validationなどで実現できず、ユーザーの入力値を都度正規化する必要がありました。
保存時と検索時で同様の正規化が実行される点はnormalizesのよい点だと私は考えます。
まとめ
ActiveRecord::Base::normalizes は、コードを短く書けるだけでなく、アプリケーションの保守性を高める上でも非常に強力な機能です。
-
責務の集約:
これまで「保存時はモデル」「検索時はコントローラー」と分散しがちだった正規化ロジックを、モデル層に一本化できます -
検索時のバグ防止:
whereなどのクエリ発行時にも自動で正規化が適用されるため、実装漏れによる「データはあるのに検索にヒットしない」といった不整合を防げます -
宣言的な記述:
before_validationなどのコールバックよりも意図が明確になり、nilガードのようなボイラープレート(決まりきったコード)も削減できます
2
最後に
今回は日々の開発の中で新たに知った・使ったちょっとした知見を記事にしてみました。記事にすることで自身の考えが整理されたり、実は理解が曖昧だった点を認識して理解度を高めることができました。
この記事が誰かの助けになれば幸いです。
参考
- Ruby on Rails 7.1 リリースノート - Railsガイド#activerecord-base-normalizesが追加
- Rails 7.1: ActiveRecord::Baseにnormalizesが追加された(翻訳)|TechRacho by BPS株式会社
- ActiveRecord::Normalization::ClassMethods#method-i-normalizes