ActiveRecordのmergeは使える子

mergeはmergeでもHashじゃなくてActiveRecord::SpawnMethodsのmergeなのです。

join先のテーブルの条件で絞り込みたい

class Entry < ActiveRecord::Base
  has_many :comments
end
class Comment < ActiveRecord::Base
  belongs_to :entry
end

例えばブログのエントリーの公開・非公開がいつでも切り替えられるとして、非公開になっているブログのコメントを抽出したいな、と思ったとします。

公開・非公開かはentriesテーブルのpublished_atカラムにDateTimeがセットされているかされていないかで分かるとすると、以下のようなコードになると思います。

Comment.joins(:entry).where(entries: { published_at: nil })

でも、entries: { published_at: nil }の部分って、もう少し表現を変えられないかな?って思いますよねー! このコードの意味するところは「この記事は公開されていない、いわば下書きの状態である」と言いたいので、その通りにコードを書きたいですよね。

抽出条件に名前をつけるにはActiveRecordのNamed Scopeという機能を使います。つまりこういうことですね。

class Entry < ActiveRecord::Base
  has_many :comments

  scope :draft, -> { where(published_at: nil) }
end

このscopeで定義した条件を利用するのに、ActiveRecord::SpawnMethods#mergeを使うことができます。mergeを使うとこんなコードになります。

Comment.joins(:entry).where(Entry.merge(:draft))

scopeを利用しない場合はこんな風に書くこともできます。

Comment.joins(:entry).merge(Entry.where(published_at: nil))

空気読んで勝手にINNER JOINしてくれれば良いのですが、そうはいかないのでjoinsは必要です。

・・・と、こんな小ネタをまとめたActiveRecord大全なんかあれば良いのにな、と思う今日この頃でした。