Association extensions
Parent has_many childrenみたいな関係のモデルでparent.childrenに対してメソッドを定義したいことがよくあります。
たとえばchildrenの属性によって絞りこんだり(children.malesで性別が男性のchildのみを返す)、またはchildrenに関連する他のモデルを取得したり(children.schoolsでchildrenが通っている学校のコレクションを返す)など。
そんなとき、Association extensionsという機能を使うと上手くメソッドを定義できます。
Association extensionとは、関連として取得するモデルのコレクション(ActiveRecord_Associations_CollectionProxy)を自分が定義したメソッドやモジュールで拡張することが出来る機能のことです。
例題のモデル定義
以下のようなモデル定義があるとします。
class Parent < ActiveRecord::Base
has_many :children
end
class Child < ActiveRecord::Base
has_many :parents
belongs_to :school
end
class School < ActiveRecord::Base
has_many :children
end
parent = Parent.first
parent.children #=> [Child, Child, Child]
parent.children.first.school #=> School
メソッドで拡張する
ではchildrenを性別が男性かどうかによってフィルタするメソッドmalesを定義して、拡張してみます。
class Parent < ActiveRecord::Base
has_many :children do
def males
self.where(:gender, 'male')
end
end
.
.
.
end
Parent.first.children.males #=> [Child, Child]
上記のように定義すると以下のようにするchildrenに対して定義したmalesメソッドを呼ぶことができます。
ちなみにmalesメソッド内でのselfはchildrenになります。
モジュールで拡張する
上記のようにメソッドで拡張することも出来ますが、childrenはSchoolモデルでも使われているため同じメソッドを重複して定義することになります。
そういうときはchildrenを拡張するモジュールを作り、そのなかでメソッドを定義すると重複を失くすことが出来ます。
module ChildrenExtension
def males
self.where(:gender, 'male')
end
end
class Parent < ActiveRecord::Base
has_many :children, -> { extending ChildrenExtension }
.
.
.
end
class School < ActiveRecord::Base
has_many :children, -> { extending ChildrenExtension }
end
Parent.first.children.males #=> [Child, Child]
School.first.children.males #=> [Child, Child]