🐈
デフォルトの時刻を定数化する時は気をつけよう
前提(フレームワーク)
Rails
やりたかっこと
レコード作成日時からxx日後を有効期限としてレコードに保存したい
起こった問題
「有効期限が常に同じ日付になる」という不具合が起こった
原因
原因はモデルで以下のように定義していた定数(デフォルト設定)のせい
class HogeModel < ApplicationRecord
EXPIRED_AT = Time.zone.now + 30.days
validates :expired_at, presence: true, default: -> { EXPIRED_AT }
end
理由
レコード作成時点からxx日後を有効期限として設定するため、その値(式)をEXPIRED_AT
として定数に切り出していました。しかしこれだと、EXPIRED_AT
にはRailsが起動した時点の値を基準に30日後が入ります。そのため「有効期限が常に同じ日付になる」ということになるわけです。
そもそも「定数」に可変となる値を定数としていることに違和感を持たなければならなかったのですが....。
対応
普通にブロック内に書くようにしました。
class HogeModel < ApplicationRecord
validates :expired_at, presence: true, default: -> { Time.zone.now + 30.days }
end
他でも色々使い回すために定数みたいに切り出しておきたい場合は、以下のようにクラスメソッドにすれば良いです。
class HogeModel < ApplicationRecord
validates :expired_at, presence: true, default: -> { default_expired_at }
def self.default_expired_at
Time.zone.now + 30.days
end
end
まとめ
当たり前だけど可変の値は定数にしない。
もしxx日を基準に開始、終了とかの時は定数にして良い。
(デフォルトでxx日後になるはずなのになんでそうならないんだ!ともがいてましたが、一旦気づくと当たり前すぎて恥ずかしかった)
Discussion