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 使わないなら意味ないけど