久々に便利なの見つけた。有名だったりするのかな?
by_starはモデルの絞り込みに使えるgemで、ActiveRecordとMongoidで使える。
ある期間内のレコードだけを表示したり集計を取ったりというときに使える。自分でも大したコードにはならないんだけど汎用的なものなのでこのgemを使うのが良いでしょう。
githubのREADMEを見れば一目瞭然なのだけど、一応紹介しておく。
Post.by_year(2013) # all posts in 2013
Post.before(Date.today) # all posts for before today
Post.yesterday # all posts in 2013
Post.between_times(Time.zone.now - 3.hours, # all posts in last 3 hours
Time.zone.now)
@post.next # next post after a given post
カラムを指定しない場合はcreated_at
が使われる。自分で定義したカラムを使いたいなら次のようにすればOK。
Post.by_year(2013, field: :hogehoge_at) # hogehoge_at という Date/DateTimeなカラムを使用
こういう絞り込みでよくあるのは○年○月のデータ一覧みたいなのだと思うのでそのやりだけ簡単に書いておく。
Post.by_year # 今年のpost
Post.by_month # 今月のpost
Post.by_month(4) # 今年の4月
Post.by_month(4, year: 2012) # 2014年の4月
自前カラムを使うならscopeにしとくと便利だと思う。
class Post < ActiveRecord::Base
scope :by_year_month, ->(y, m) {
by_month(m, year: y, field: :hogehoge_at)
}
end
実装を見てみると最終的にはbetween_times_query
ていうメソッドに行き着くっぽかった。
def between_times_query(start, finish, options={})
start_field = by_star_start_field(options)
end_field = by_star_end_field(options)
scope = by_star_scope(options)
scope = if options[:strict] || start_field == end_field
scope.where("#{start_field} >= ? AND #{end_field} <= ?", start, finish)
else
scope.where("#{end_field} > ? AND #{start_field} < ?", start, finish)
end
scope = scope.order(options[:order]) if options[:order]
scope
end
単機能をいい感じに切り出せてて良さげです。この発想は見習うとこありそう。