Rails 4: STI belongs to polymorphic STI

Its hard to find a good example how to use single table inheritance (STI) and polymorphic relation combined in Rails 4. I did not find one when I need it. So I decided to share with you my working example. It does not explain anything but it gives you a working code you may want to investigate in solving your own task.

What we have:

  • Group — AR model inherited from other model (STI)
  • Member — AR model with polymorphic association to any source.
  • GroupMember — inherited from Member (STI). Belongs to Group via polymorphic source relation.

Why all this?

I want one model/db_table Member I can re-use for group, project, team membership.

  • I want GroupMember, ProjectMember classes inherited from Member for prettier and cleaner code.

So I started from this non-working code:

# Table: members
# source_id: integer
# source_type: string
class Member < ActiveRecord::Base
belongs_to :source, polymorphic: true
end

class GroupMember < Member
# we want to re-use existing source association.
belongs_to :group
end

class Group < Namespace
# I want query only GroupMembers that belongs to this group
has_many :group_members
end

And ended with this working code:

class Member < ActiveRecord::Base  
belongs_to :source, polymorphic: true
end

class GroupMember < Member
belongs_to :group, class_name: 'Group', foreign_key: 'source_id'
end

class Group < Namespace
has_many :group_members, as: :source, class_name: 'GroupMember'
end

One interesting thing you may want to know: Rails puts the Parent STI class name in source_type. In our case it will be Namespace instead of Group because Group inherited from Namespace

group = Group.first  
member = GroupMember.new
member.source = group
puts member.source_type
# => Namespace

Originally published at blog.dzaporozhets.me on September 16, 2014.