maeshimaの日記

メモ書きです

has_manyの :extend オプション

メモ。Rails2.3.8のドキュメントより。

has_many のブロック中にメソッド定義することで、関連で使えるメソッドを追加できる。

  class Account < ActiveRecord::Base
    has_many :people do
      def find_or_create_by_name(name)
        first_name, last_name = name.split(" ", 2)
        find_or_create_by_first_name_and_last_name(first_name, last_name)
      end
    end
  end

  person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
  person.first_name # => "David"
  person.last_name  # => "Heinemeier Hansson"

複数の関連中で共通に使うようなメソッドがある場合、モジュールにまとめて:extendオプションで指定することでDRYにできる。

  module FindOrCreateByNameExtension
    def find_or_create_by_name(name)
      first_name, last_name = name.split(" ", 2)
      find_or_create_by_first_name_and_last_name(first_name, last_name)
    end
  end

  class Account < ActiveRecord::Base
    has_many :people, :extend => FindOrCreateByNameExtension
  end

  class Company < ActiveRecord::Base
    has_many :people, :extend => FindOrCreateByNameExtension
  end

配列に入れることで複数のモジュールを使える。同名のメソッドが存在した場合、配列の後にあるモジュールのメソッドの方が有効になる。

  class Account < ActiveRecord::Base
    has_many :people, :extend => [FindOrCreateByNameExtension, FindRecentExtension]
  end

関連のブロック中や、extendオプションで指定されるモジュール中で、関連元のオブジェクトなどを取得するためのproxy系メソッドが3つある。

proxy_owner
関連元のオブジェクト
proxy_reflection
関連オブジェクト
proxy_target
関連先のオブジェクト。キャッシュされてない限りはemptyらしい