I really like the idea of Single Table Inheritance (STI) for all sorts of applications to keep code DRY and make it easier to organize object behavior. The only problem is that Rails 3.0.3 doesn’t fully support STI with association collections.
Let’s say you have a User model that has many badges. The badges will be stored in the badges table but you want to implement each badge in a subclass. All you have to do is make sure there’s a :type
field of type string in your badges table and Rails STI support should take care of the rest (well, in theory).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Now you can do cool things like create a new Superhero badge and add it to a user’s badge collection.
1 2 3 |
|
But for some weird reason, you can’t use the best practice of building a badge directly from the user’s badges collection.
1 2 3 |
|
This is particularly annoying if you’re trying to create new badges from a form where :type is a drop down menu.
The reason the collection build method doesn’t work as expected is because :type is a protected field and ActiveRecord::AssociationReflection doesn’t fully support STI (at least in Rails 3.0.3).
Not to fret, hacks to the rescue!
You have two options to make STI work as expected.
Option 1: Override the Badge.new method to handle :type
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Option 2: Patch AssociationReflection to behave more intelligently
1 2 3 4 5 6 7 8 9 10 11 12 |
|
My preference is Option 2 even though it might break in future releases of Rails. I’d rather have Rails behaving as expected than pepper my models code with repetitive hacks.
The above solutions were inspired from a couple of different posts and sources.
I submitted Option 2 as a patch for Rails.