Jay Fields has been blogging about using
presenters in Rails. I came across an example in a website I am working on which clearly demonstrates their usefulness.
If context helps, this is for a photography website.
def review
@order_items = current_order.order_items.find(:all)
@items_by_image = @order_items.group_by(&:image_id)
@images = Image.find(@items_by_image.keys)
@applied_packages = current_order.best_available_packages
@available_packages = current_order.event.available_packages.find(:all) - @applied_packages
end
That controller action may look fairly normal; I'm simply setting some instance variables for use in the view. What's the problem?
The first problem is that this action has too much noise. Although five lines is not many, all I am doing is loading some data to present in the view. Sure, I could reduce this by letting the view call current_order.best_available_packages instead of assigning that to an instance variable, but that doesn't seem right either. This code needs to go somewhere else.
The second problem is with testing. To test that controller action with mocks, I need to mock/stub quite a bit. Also, testing one small piece of functionality is difficult. Because all five instance variables are set in one method, I cannot, for example, only test the @available_packages assignment (at least not easily).
I haven't actually made this into a presenter yet, but if you're wondering what it might look like:
class OrdersController < ActionController::Base
def review
@presenter = OrderReviewPresenter.new(current_order)
end
end
class OrderReviewPresenter
def initialize(order)
@order = order
end
def order_items
@order.order_items.find(:all)
end
def items_by_image
@order_items.group_by(&:image_id)
end
def images
Image.find(items_by_image.keys)
end
def applied_packages
current_order.best_available_packages
end
def available_packages
current_order.event.available_packages.find(:all) - applied_packages
end
end
This solves both of my problems. It's so much easier to test as a presenter, and look at how skinny that controller is!
In addition to this example of a presenter for simply displaying data, presenters have other uses too. Read
Jay's blog post for more information.
Steven Wisener gave me feedback that he thinks presenters are solely part of the view. I agree. Even though the controller is instantiating the presenter and the view is using it, the logic that goes into presenters is all view logic. Therefore presenters aren't really between the controller and the view, they're simply part of the view.