wyclin
wyclin

Reputation: 522

Rails - Polymorphism in single table inheritance

I have three models, Poem and Song and User. Users can vote on any number of poems and songs.

One solution would be to make two association models PoemVote and SongVote

class PoemVote
  attr_accessible :poem_id, :user_id
  belongs_to :poem
  belongs_to :user
end

class SongVote
  attr_accessible :song_id, :user_id
  belongs_to :song
  belongs_to :user
end

I can call some_poem_vote.poem and some_song_vote.song

However, PoemVote and SongVote are essentially the same. How do I use single table inheritance to extend the two from one parent Vote class?

I'm thinking something along these lines:

class Vote
  attr_accessible :resource_id, :user_id
end

class PoemVote < Vote
  ...not sure what goes here...
end

class SongVote < Vote
  ...not sure what goes here...
end

How do I make it work so that I can still call some_poem_vote.poem but underneath have PoemVotes and SongVotes share one database table? Or is there a better solution for my problem?

Upvotes: 2

Views: 2786

Answers (1)

m_x
m_x

Reputation: 12554

in rails, STI is simple : you just create a type string column on your votes table, and rails takes care of the rest. To create the right associations, you can do something like :

class Vote
  attr_accessible :user, :votable
  belongs_to :user
  belongs_to :votable, polymorphic: true
end

...which would require to add a votable_id and a votable_type column on your votes table. Be sure to add

has_many :votes, as: :votable, class_name: 'PoemVote' # or 'SongVote'

on your associated model. However, problem with this approach is that you have to be vigilant and not use Vote directly to create votes, or you will have votes of the wrong type associated. To enforce this, there is a possible hack :

class Vote
  attr_accessible :resource_id, :user_id

  def self.inherited( subclass )
    super( subclass )
    subclass.send :belongs_to, :votable,
                  class:  "#{subclass.name.gsub('Vote','')}"
  end
end

... but i know for sure (i struggled with te same problem) that it opens the door for code horror, as you have to resolve a lot of problems caused by inheritance (scopes behave weirdly, some libs don't manage STI well, etc.).

The question is: do you really need STI ? if your votes behave the same, don't bother using STI, just use a polymorphic belongs_to, you'll save yourself a lot of headaches.

Upvotes: 4

Related Questions