I have recently transferred to a new job. I am now working as a Ruby on Rails developer for TwitMusic. Before I go on about promoting my new employer (which I won’t do for the sake of this article), I would like to point out that I am being trained for the position before I get involved with the production code. For the first part of my training, my employer gave me a set of problems about Ruby on Rails that I have to solve on my own. One of the most challenging problems I encountered was about polymorphic associations.
In Rails, Polymorphic Associations allow an ActiveRecord object to be associated with multiple ActiveRecord objects. A perfect example for that would be comments in a social network like Facebook. You can comment on anything on Facebook like photos, videos, links, and status updates. It would be impractical if you were to create a comment model (photos_comments, videos_comments, links_comments) for every other model in the application. Rails eliminates that problem and makes things easier for us by allowing polymorphic associations.
For this article, I have 2 independent models: Foo and Bar:
First of all, let’s create the Comment model. Normally, if we were just create a comments model for a single model like Post, we would have an integer field called post_id to store the foreign key. Since we have multiple models, we need to have it reference to something more abstract. As I understand right now, Rails allows us to make an interface for polymorphic associations. Also, all of the models we’re adding comments to have one thing in common: they allow comments. With that in mind, we’ll create a foreign key field called
commentable_id. The comment also needs to know which model it’s associated with, so we’ll create another field called
Once that’s done, we’ll need to associate the comment model with the other models.
Instead of making it belong to a specific model, we made it belong to
commentable, which will be the interface for the other models to associate with.
We establish the association by making it in the other models as well:
Now that the relationships are set up, it’s time to create the controller for our Comment model:
Afterwards, we need to modify
routes.rb to reflect the association.
What we want to do is to have all the comments displayed if we go to /foos/1/comments. Here’s where it gets tricky: we can’t do the usual with the index action for the comments controller. If we do our usual:
All the comments for every model will be displayed. We need to find a way to only display the comments under the model that we are referring to.
As seen in the RailsCasts episode about Polymorphic Associations, we have to create a method to solve our problem:
The RailsCasts episode explains this method pretty well, so I won’t go over this. Now we can use this method in the index action to get the corresponding comments.
Of course, we’ll want to make a form to make use of our new functionality, so users can add their own comments.
When the form is submitted, it will call the create action of the CommentsController, so let’s create that.
find_commentable again to get the corresponding model, so we can make sure that the comment saved will be pointing to the right model. Another thing to note is that we are redirecting to
:id => nil, to make it redirect back to the current page.