Railsのアソシエーションで、中間テーブルを経由して一個飛ばしでインスタンスを取得できるhas_many~through便利ですよね。ところが、この中間テーブルがpolymophicだった場合に、どうやって対象を取得すれば良いのかということに悩みました。
解決策は、結論からいうと、下の例でいうと モデルにはこういう形で定義して、そのままuser.post_commentsのような形でできます。
has_many :post_comments, through: :comments, :source => :commentable,:source_type => 'Post'
has_many :blog_comments, through: :comments :source => :commentable,:source_type => 'Blog'
図やコードを交えて解説
これだけではなんのことかよくわからないかもしれないので、上の結論のコードの全容を明かすと以下のようになります。
以下のモデルは、複数のユーザがブログを作り、各々ポストを投げていくという世界を想定したモデル群です。

class User < ActiveRecord::Base
has_many :blogs
has_many :posts
has_many :comments
has_many :post_comments, through: :comments, :source => :commentable,:source_type => 'Post'
has_many :blog_comments, through: :comments :source => :commentable,:source_type => 'Blog'
end
class Blog < ActiveRecord::Base
has_many :comments, as: :commentable
end
class Post < ActiveRecord::Base
has_many :comments, as: :commentable
end
class Comment < ActiveRecord::Base
belongs_to :commentables, polymophic: true
end
コードを見てみると、確かにいつものようにhas_manyで「Userが行った投稿を取ってくる」ことや「Userが持っているブログを取得する」ということは簡単にできます。ただ、「ユーザーがコメントした投稿」や「ユーザーがコメントしたブログ」についてはどうやって取得しようか少し迷うところがあると思います。
特に、ここでは、中間テーブルの役割をはたすCommentがポリモーフィック関連なのでなおさらです。
railsのポリモーフィック関連では、able_idとable_typeというカラムで関連が定義されますが、上のコードを例にとるとhas_many :commentsだけでは、「ユーザーがコメントした投稿」と「ユーザーがコメントしたブログ」の両方ともが取得されてきてしまいます。愚直にやるのであれば、commentable_typeを条件にSQLを発行してとやればできなくはないですが、それは苦しいです。。
そこで、この解決策では、:sourceオプションと:source_typeオプションを使って、ポリモーフィック関連の先のテーブルを指定してあげることで「ユーザーがコメントした投稿」と「ユーザーがコメントしたブログ」を簡単に取得できるようにしています。
polymophicなテーブルを経由して、has_many thourghする場合は、sourceオプションでポリモーフィック関連の名前を指定して、source_typeで中間テーブルの先のクラスを指定してシンプルにデータをとってきましょう。