Search code examples
ruby-on-railsactiverecordmodelmany-to-manyhas-many-through

Ruby on Rails – Models with several many-to-many associations


I am currently developing a Dungeons & Dragons 4th Edition Character Sheet application in Ruby on Rails 3.2.1. I am currently at a stage where I realize that I need to associate character races (dwarves, elves, …) with ability bonuses. (Example: a dwarf gets +2 bonuses to the Constitution and Wisdom abilities as a racial trait.)

I currently have the following set up:

class Character < ActiveRecord::Base {
  has_many :attributions
  has_many :abilities, through: :attributions
}

class Attribution < ActiveRecord::Base {
  belongs_to :character
  belongs_to :ability

  # The "attributions" table also has the column "score,"
  # which is the character's ability score
}

class Ability < ActiveRecord::Base {
  has_many :attributions
  has_many :characters, through: :attributions
}

As you can see, the characters each have their own set of abilities. I figure I may do the same thing for character races, but I am unsure of any best practices in this area. I could probably use the same join model, creating something like this (Character would stay the same, except for an overridden abilities method, perhaps):

class CharacterRace < ActiveRecord::Base {
  # I am not sure if this will actually work, but I hope
  # you understand what I am trying to do with this
  has_many :racial_ability_traits, class_name: "Attribution"
  has_many :abilities, through: :racial_ability_traits
}

class Attribution < ActiveRecord::Base {
  # The following two are just like before
  belongs_to :character
  belongs_to :ability

  # This field would be new
  belongs_to :character_class
}

class Ability < ActiveRecord::Base {
  # These are the same as above
  has_many :attributions
  has_many :characters, through: :attributions

  # These would be new, and I am not sure if it will work, but
  # I hope you understand what I amm trying to do with this
  has_many :racial_traits, class_name: "Attribution"
  has_many :character_races, through: :racial_ability_traits
}

However, even if this would work, I somehow feel like having the same join model for different joins (even if the purpose is pretty much the same) is kind of an ugly approach. I could, of course, create a different kind of join like this:

class CharacterRace < ActiveRecord::Base {
  has_many :abilities, through: :attributions

  # These are for the racial bonus
  has_many :racial_ability_bonuses
  has_many :abilities, through: :racial_ability_bonuses
}

class RacialAbilityBonus < ActiveRecord::Base {
  belongs_to :character_race
  belongs_to :ability
}

class Ability < ActiveRecord::Base {
  has_many :attributions
  has_many :abilities, through: :attributions

  # These are for the racial bonus
  has_many :racial_ability_bonuses
  has_many :character_races, through: :racial_ability_bonuses
}

This would probably work, but there is a problem. Not only the races can have such traits/bonuses. A magical item might also give an ability bonus. Therefore, an Item model should also be given a has_many :abilities, through: :item_ability_bonus association. Continuing down the road explained above, this would cause a lot of join models.

I am therefore asking you guys if you know any good approach for handling this problem. Any suggestion about making my existing code better is welcome as well.

I am very thankful for all of your serious answers. :-)


Solution

  • I think you just need polymorphic associations.