Topic: Polymorphic DB Model

Hello!

I've been cracking my head on this for a bit, and I'm ready to plead for help.

I'm trying to create a Polymorphic model for Effect types in my card game.

class Card < ActiveRecord::Base
    has_many :effects, :through => :is_effect

class AddSubtractEffect < ActiveRecord::Base
    belongs_to :card
    belongs_to :is_effect, :polymorphic => true


Am I going about this the wrong way?  Is it possible to load up all the different effects that apply to a card using a polymorphic active record model?

Thanks in advance,

Re: Polymorphic DB Model

The polymorphic association is meant to go the other way around. It should be used on the belongs_to side, not the has_many side.

For example, if you have a Comment model, and you want it to belong_to any one of several models, polymorphic association would be a perfect fit.

But here you want many different models through has_many, so polymorphic association will not work in this case.

Are all of the effects that different? Do they each need their own table? If not, then single table inheritance will be a much better solution. You simply create an "effects" table with a column "type" then do this:

class Card < ActiveRecord::Base
  has_many :effects
end

class Effect < ActiveRecord::Base
  belongs_to :card
end

class AddSubtractEffect < Effect

end

Last edited by ryanb (2006-11-28 14:24:12)

Railscasts - Free Ruby on Rails Screencasts

Re: Polymorphic DB Model

I'm not entirely sure where you're going with this.  Does it work without having to do the polymorphism?

You'd need polymorphism only if you were going to have Effects belong to Cards as well as belong to something else.  Polymorphism simply means that a model carries information for both 1) the id of the thing it belongs to and 2) the class of the thing it belongs to.

Re: Polymorphic DB Model

The intent here was to make a flexible model for having one or more different effects that are related to a Card.

My idea was to make each Effect be it's own model and implement a method like do_effect() that took the xml gamestate in and applied it's effect type to an attribute in it.  The reason I wanted to make each Effect be it's own model was so that I could create n number of effect types and specify their behavior and schema independently.

So I was hoping that when the relationship was complete, I could do something like:

@effects = Card.effects.find(:all)

for effect in @effects
  effect.do_effect(@gamestate)
end


I don't mind if each effect type doesn't inherit from a base class, I just wanted an easy collection to loop through.

Re: Polymorphic DB Model

How different do you expect the schema to be for each effect? If you don't expect it to be very different, than the Single Table Inheritance approach I gave above should work well.

If the schema does vary a lot per effect, than you may want to keep each effect a separate model/table and create an in-between model which joins the Card and the many effects through polymorphic association. I can try to provide some code if you need it.

Railscasts - Free Ruby on Rails Screencasts

Re: Polymorphic DB Model

I do see your point about using Single Table Inheritance, and I think for the vast majority of effect types, it will work well.  I imagine that I will be using a combination of both techniques to accomplish what I'm trying to do.

If you do have some example code for joining the Card to effects through polymorphic association, it would be greatly appreciated!  I can't tell you how many permutations I went through trying to get this to work!

Thanks in advance.

Re: Polymorphic DB Model

Hmm, I was initially thinking of using has_many :through in combination with polymorphic association. But, apparently that doesn't work. Some alternatives are given at the bottom of that article, but I don't know if they are satisfactory.

You could try this:

class Card < ActiveRecord::Base
  has_many :effects
end

class Effect < ActiveRecord::Base
  belongs_to :card
  belongs_to :processor, :polymorphic => true

  def do_effect(...)
    processor.do_effect(...)
  end
end

class AddSubtractEffect < ActiveRecord::Base
  def do_effect(...)
    # ...
  end
end


In this case the generic Effect join model implements the do_effect method and simply passes it onto the proper effect processor (specified through polymorphic association). You may want to rename "processor" to something else.

I don't like the solution as much as STI because you need to keep track of the generic Effect model which acts as a join model, but at least each effect can have its own schema/database this way.

Railscasts - Free Ruby on Rails Screencasts

Re: Polymorphic DB Model

Thanks very much for your help.  That works great! 

I also looked again at the link you provided, and found at the tail end of that page a plugin that I'm looking at as a third option.

http://blog.evanweaver.com/articles/200 … polymorphs