ActiveRecord の scope で時刻が一度しか評価されない

表題の現象を確認するために、次のような scope を記述します。

class Hoge < ActiveRecord::Base
  scope :fuga, where(["some_time <= ?", Time.now])
end

これは文法のエラーを起こさないので正しく動作しているように見えますが、実際にはアプリケーションサーバがモデルをメモリ上にロードしたタイミングで Time.now の値が一度だけ評価され、後はその値が固定で用いられるようになります。

これを回避するには lambda を用いればいいようです。

class Hoge < ActiveRecord::Base
  scope :fuga, lambda{where(["some_time <= ?", Time.now])}
end

Ruby on Rails Guides にもしっかり記載されているのですが、テストや運用からしばらく時間が経たないと気がつきにくいので要注意ですね。

http://guides.rubyonrails.org/active_record_querying.html#working-with-times