date_validator gem を使ったバリデーションにおいてオプションで Date.current を指定する際に気をつけること
date_validator に限らず validations のオプションの指定の仕方に注意が必要なやつ。
何が問題か
Rails の validation のオプションで validates :birthday, date: {before_or_equal_to: Date.current}
のような指定をしていて、
(要するに今日より未来の日付で誕生日の指定はさせない的な)問題が起きた。
この記述だと、サーバーが起動したタイミングで Date.current
が固定されてしまうので、
サーバー起動日より後の日付で、当日を birthday
に指定しようとすると、バリデーションエラーになってしまう。
対策
オプションは Proc オブジェクトで指定しておく。 つまり、バリデーションが走るタイミングで動的に評価されるようにしておく。
原因
lambda, ->
がダメな理由
ダメなわけじゃなく、内部的な実装の問題
Procであれば本来的には問題ない
- まず
date: {before_or_equal_to: Date.current}
のバリデーション自体がdate_validator
Gem の機能です - この部分の具体的な処理は
vendor/bundle/gems/date_validator-0.8.1/lib/active_model/validations/date_validator.rb
def validate_each
の中でオプションの分解を L.71 で開始- この時 Proc オブジェクトであれば
.call
で実行するのだけど、問題は.call(record)
という形の呼び方なので-> { Date.curret }
だと、引数の数チェックで引っかかってエラー-> (record) { Date.current }
なら落ちない( record 使わないなら意味ないけど