久々に便利なの見つけた。有名だったりするのかな?

radar/by_star

by_starはモデルの絞り込みに使えるgemで、ActiveRecordとMongoidで使える。

ある期間内のレコードだけを表示したり集計を取ったりというときに使える。自分でも大したコードにはならないんだけど汎用的なものなのでこのgemを使うのが良いでしょう。

githubのREADMEを見れば一目瞭然なのだけど、一応紹介しておく。

1Post.by_year(2013)                           # all posts in 2013
2Post.before(Date.today)                      # all posts for before today
3Post.yesterday                               # all posts in 2013
4Post.between_times(Time.zone.now - 3.hours,  # all posts in last 3 hours
5                   Time.zone.now)
6@post.next                                   # next post after a given post

カラムを指定しない場合はcreated_atが使われる。自分で定義したカラムを使いたいなら次のようにすればOK。

1Post.by_year(2013, field: :hogehoge_at) # hogehoge_at という Date/DateTimeなカラムを使用

こういう絞り込みでよくあるのは○年○月のデータ一覧みたいなのだと思うのでそのやりだけ簡単に書いておく。

1Post.by_year # 今年のpost
2Post.by_month # 今月のpost
3Post.by_month(4) # 今年の4月
4Post.by_month(4, year: 2012) # 2014年の4月

自前カラムを使うならscopeにしとくと便利だと思う。

1class Post < ActiveRecord::Base
2  scope :by_year_month, ->(y, m) {
3    by_month(m, year: y, field: :hogehoge_at)
4  }
5end

実装を見てみると最終的にはbetween_times_queryていうメソッドに行き着くっぽかった。

 1def between_times_query(start, finish, options={})
 2  start_field = by_star_start_field(options)
 3  end_field = by_star_end_field(options)
 4  scope = by_star_scope(options)
 5  scope = if options[:strict] || start_field == end_field
 6            scope.where("#{start_field} >= ? AND #{end_field} <= ?", start, finish)
 7          else
 8            scope.where("#{end_field} > ? AND #{start_field} < ?", start, finish)
 9          end
10  scope = scope.order(options[:order]) if options[:order]
11  scope
12end

単機能をいい感じに切り出せてて良さげです。この発想は見習うとこありそう。