Using Presenters in Rails

Sitting thrugh my first tutorials session at RailsConf we've come across an interesting discussion about the philosophy of using the 'Presenter Pattern' to refactor code in your applications controller.

What the presenter initially did was build out a traditional presenter class to extend the model and provide two methods to find possible status and priority objects from the database. This requires the author to delegate class methods from the model that the Presenter class could use. It also requires us to wrap any instance variable of the child object in the presenter class before we pass it to the view.

class StoryPresenter
  
  def initialize(story)
    @story = story
  end
  
  delegate  :id, :class, :errors, :to_param, :new_record?,
            :to => :@story
  
  def possible_statuses
    @statuses = Status.find(:all).map{ |s| [s.name, s.id] }.unshift []
  end
  
  def possible_priorities
    @priorities = Priority.find(:all).map{ |e| [e.name, e.id] }.unshift []
  end
  
  def method_missing(name, *args)
    @story.send name, *args
  end
  
end

And then the implementation in the controller looks something like this:

def create
  story = @project.stories.build(params[:story])
  respond_to do |format|
    format.js do
      @story = StoryPresenter.new story
      render :action => "stories/new" unless story.save
    end
  end
end

A member in the audience suggested that instead of treating the presenter as a traditional presenter object we simply write it as a module. Then we no longer would have to delegate the methods from the model and instead of wrapping any instance variable of the child class we could just extend it. The argument for this is clear. It's less code, and it's more flexible.

module StoryPresenter
  
  def possible_statuses
    @statuses = Status.find(:all).map{ |s| [s.name, s.id] }.unshift []
  end
  
  def possible_priorities
    @priorities = Priority.find(:all).map{ |e| [e.name, e.id] }.unshift []
  end
  
end

And then the implementation in the controller looks something like this:

def create
  @story = @project.stories.build(params[:story])
  respond_to do |format|
    format.js do
      @story.extend StoryPresenter 
      render :action => "stories/new" unless @story.save
    end
  end
end

However, the arguments against using a presenter as a mixin are more philosophical. If we're using the presenter pattern we should encapsulate only what we need the view to use explicitly to prevent logic leaking into the view or the controller by other authors. By treating the Presenter object as a class and explicitly delegating the model methods we do just that. To utilize further methods of the model we would need to return to the presenter object and extend it there as opposed to just sliding extraneous logic into our controller or view.