"Send this page to friend" with polymorphic controller in Rails

Recently I wrote about Tableless models in Ruby on Rails, giving an example of Recommendation model which could be used to validate users' recommendations. Today I will extend that example so that it will be more complete.

What the mentioned solution lacks is a controller handling recommendation actions. Let's first make Article and Photo models recommendable ones by defining their associations:

has_many :recommendations, :as => :recommendable

The models are ready, so let's create a controller.

class RecommendationsController < ApplicationController
  def new
    if !params[:photo_id].nil?
      @recommedable = Photo.find params[:photo_id]
    elsif !params[:article_id].nil?
      @recommedable = Article.find params[:article_id]

    raise ActiveRecord::RecordNotFound if @recommendable.nil?

  def create
    @recommendable = Recommendable.new :params[:recommendation]

    if @recommendable.valid?
      # send it via e-mail
      flash[:notice] = 'Your recommendations has been processed'
      redirect_to @recommendation.recommendable
      render :action => :new

We're almost there, but there's more Rails can do for us here. Let's define some new routes.

# config/routes.rb
map.resources :recommendations

map.resources :articles do |a|
  a.resources :recommendations

map.resources :photos do |p|
  p.resources :recommendations

Now we're able to use those pretty helpers in our views that will generate nice-looking URLs (like: article/1/recommendation/new).

<%= link_to 'recommend this article', new_article_recommendation_path(@article) %>

The link above will take us to the 'new' action of RecommendationsController. The last thing we need to do is to render the form, so that user can comment his recommendation.

<% form_for @recommendable.recommendations.build do |f| %>
  <%= f.hidden_field :recommendable_type, :value => @recommendable.class %>
  <%= f.hidden_field :recommendable_id, :value => @recommendable.id %>
  <%= f.text_field :email %>
  <%= f.text_area :body %>
  <%= f.submit 'Do it!' %>
<% end %>

There are many more cases of use polymorphic controllers (with normal or tableless models) than sending recommendations. Commenting, ranking, abuse-reporting, tagging... The rule is that if you have a model that is associated to other models via polymorphic association, you should consider using polymorpic controller to manage it. Not only Rails will do much work for you behind the scenes, but the structure of your application will stay clear both inside (reusable code) and outside (nice links).

Challenges and Solutions in Developing English-Arabic Applications
Python vs. Java - When to Use Each Language
5 Lessons from Transforming a Startup Into an Enterprise-Grade Product