Nwht0xn1

ActiveRecordで子レコードを作成するときに親レコードのインスタンス変数を参照したい場合はinverse_ofが必要Created on 2016-06-07 by r7kamura

ActiveRecordの inverse_of オプションは Rails Guides でも紹介されているが、実際に使う機会になるまでなかなかその必要性を理解しにくいので、WikiHubで必要になった例を紹介する。WikiHubでは、Wikiページとそのページの編集履歴をDBに保存するために、以下のようなモデルクラスを用意している。

class Page < ActiveRecord::Base
  has_many :commits, inverse_of: :page
  attr_accessor :modifier
  after_create :create_commit!

  private

  def create_commit!
    commits.create!
  end
end

class Commit < ActiveRecord::Base
  belongs_to :page, inverse_of: :commits

  before_validation :assign_user

  private

  def assign_user
    self.user ||= page.modifier
  end
end

Pageは1ページを表し、Commitは1回の編集内容を表す。Pageは複数人が編集するものなので、Userに関する情報は保存しないが、代わりに個々のCommitに編集したUserに関する情報が保存される。このとき、UserはPageのインスタンス変数 (attr_accessor) を介して受け渡される。

上記のコードでは inverse_of オプションを利用した。この指定が無いと、Commitクラスのインスタンスを生成するときに、関連するPageクラスのインスタンスも新しく作成されてしまう。そうなると、元々のPageクラスのインスタンスに入っていた modifier の値が引き継がれず、新しく生成したCommitクラスのインスタンスから参照するとnilになってしまう。ちなみに、inverse_of オプションに指定する値は、「関連先のモデルから見て自分のモデルが何と呼ばれるか」を指定する。Pageの場合は commit.page のように :page と呼ばれるのでそれを指定する。

ActiveRecordで子レコードを作成するときに親レコードのインスタンス変数を参照したい場合は、inverse_of オプションを利用することを忘れないようにしないといけない。