Simple Presenters

Courtenay : August 23rd, 2007

The Presenter pattern, as my limited monkey-brain can see it, is a way of encapsulating a bunch of logic and display-related code for a database record.

If you want to be truly confused, go check out what the venerable Martin Fowler (who once ignored me in an elevator) has to say about it: Supervising Presenter and Passive View. As usual with Java people, it's horribly complex.

In Rails, this is the way our requests currently work:

CMV

The request comes in, hits the controller, we load up some data from the model, it gets pushed to the view, and then we use a combination of helpers and lots of conditional and other stuff that looks like PHP to malleate it until it looks good.

Unfortunately for my simple chimpanzee neurons, this all feels wrong. Here's some sample 'wrong' code I just paraphrased from a live app:

<% if @cart_item.variation.nil? or @cart_item.variation.product.deleted? or ... %>
  <img src="/images/active.png" /> This cart is no longer active.
<% end %>

OK, this one is easy to refactor. You just add a method to CartItem model, like

def is_active?
  variation.nil? or variation.product.deleted? ...
end

Or, you could write a helper method:

module CartItemHelper
   def show_active(cart_item)
     image_tag("active") + " This cart is no longer active" if cart_item.is_active?
   end
end

Ugh. Capital Ugh. First off, the is_active? method is nice enough, but it's all view logic! This method is being used to control the logic in the presentation layer. What is it doing in the model? I think of the model as entirely database related.

This is where the "presenter" comes in. If you've used the Liquid template/layout system, you'll be familiar with Drops. Basically, a presenter contains any ruby code related to displaying fields or logic. I'll let the code do the talking:

class CartItemPresenter < Caboose::Presenter

   def name
     h(@source.product_name)
   end

   def product_link
     if item.variation.nil?
       name
     else
       link_to name, fashion_product_path(item.product)
     end
   end

  def is_active?
    @source.variation && @source.variation.product
  end

  def inactive_button
    return if is_active?
    image_tag('active') + " This product is not active."
  end

end

Pretty straightforward. In the controller, you finish with @cart_item = CartItemPresenter.new(@cart_item, self)

Here's what Presenter looks like.

class Presenter
  include ActionView::Helpers::TagHelper # link_to
  include ActionView::Helpers::UrlHelper # url_for
  include ActionController::UrlWriter # named routes
  attr_accessor :controller, :source # so we can be lazy

  def initialize(source, controller)
    @source = source
    @controller = controller
  end

  alias :html_escape :h
  def html_escape(s) # I couldn't figure a better way to do this
    s.to_s.gsub(/&/, "&amp;").gsub(/\"/, "\&quot;").gsub(/>/, "&gt;").gsub(/</, "&lt;") #>
  end  

end

So, your view no longer accesses the database directly; everything goes through the presenter. Your views now will contain very little logic; in fact, they may start to feel a little more like ASP.Net.

Here's how the new stack looks:

CPV

And the view:

<%= @cart_item.product_link %>
<%= @cart_item.inactive_button %>

What do you think? Is this layer of abstraction necessary, or do you prefer keeping things together in the model (database!) and presenter (view!) ?

P.s. I often write "todo" titles for articles and save them as drafts, so I can come back later. Seems like one slipped out.

23 Responses to “Simple Presenters”

  1. Eric Hodel Says:

    This gives me warm fuzzies for refactor potential.

  2. szeryf Says:

    Any suggestions about organizing the presenters (and their tests) in standard Rails project directory tree? Are there any tools that support this?

  3. Pratik Says:

    As long as we're talking MVC, isactive?() should be in model imho. That's what models are there for, to add meaning to raw data and provide a better domain specific interface. I can't see anything about isactive?() that would make it a pure view related method. It could very well be used in other contexts as well.

  4. Morgan Roderick Says:

    So you're adding more complexity to the system, while making your view even harder to figure out? ;-)

    If you hand over your view to someone else, will it be immediately apparent to them that the link and inactive_button actually has their own logic, that they are infact mutually exclusive, and only one will be rendered? How much investigation would it require, to figure out what's actually going on?

    Complexity like that is one of the features I really loathe about ASP.NET and the likes of it. IMHO, when you're reading code, any code, it should be immediately apparent what it does.

    I agree with Pratik on is_active belongs to the model. Consider the fact that something other than the view could need to know whether or not a model object can be considered active.

    But, I do see potential for Presenter to make it a lot easier to write tests against your view code. As with all design patterns, it takes awhile to get your head around it, and some experience to know when to use it, and when not to use it.

    It's certainly something to keep an eye on :-)

  5. johan-s Says:

    Presenters really shine imho when you're dealing with multi model stuff, where you'd have these hairy controllers and models that might not tie up well together as one common domain (think micro domainsor whatever you'd wanna call them).

    But I kinda abuse the MVC pattern and presenter term, since my "presenters" usually are more about mini-domain things, eg a single goal/problem I want to wrap up in an object, for instance a complex account registration that's easier to test without going through a (rails) functional test, and that, as an object, represent what it's actually doing as a whole. Just don't go overboard with it for small things (yagni)

    Check out Jay Fields blog for his presenter posts, good stuff there

  6. Tim Case Says:

    What's wrong with helpers?

  7. PJ Hyett Says:

    I've been reading various things about presenters over the last year and I still don't get it.

    It feels like added complexity and confusion with extremely little benefit.

  8. Taylor Singletary Says:

    I think this way of looking at things is actually very useful. The Ruby Reports system, ruport, has some presentation logic that is very similar to this -- allowing you to filter presentation before outputting to HTML, PDF, CSV, or whatever your needs are.

    I can't count how many times I've written field beautification methods in my model primarily for view (or formatting for an email) purposes and felt gross while doing it. Any attempts to factor it out challenged understandability and readability by being too clever.

    But this presentation logic seems like a rightful step in evolving view logic, that could compliment whatever end result view you have, just like Ruport.

    The end result of what I've found with any re-imagining of views from the traditional HTML and erb perspective is that I end up trying it but not liking it, and falling back to the convention, knowing that someone who follows me will also grok it.

    Eye open on this though.

  9. Ryan Bates Says:

    Thanks for the article. The presenter pattern is useful to know, but I don't think this example qualifies for it. The is_active? method should go in the model, it is acting on the model's data and doesn't have any view logic IMO. The other methods in the given presenter look like they should either go in the helper or back in the view. No sense in making things more complicated than they need to be.

  10. Jason L Perry Says:

    Ok, so the presenter pattern is cool, but how is it different from what I can do in vanilla view helpers?

  11. John Says:

    I've used this basic pattern in the past, however I felt that the presenter should be called a little more explicitly in the view:

       @item = Item.find(:first)
       <%= present(@item).method %>
    

    The present helper method would instantiate the presenter object on the model object. Also, because this is really view logic, I feel it's best to keep it away from the controller. You don't want to load up your HTML presenter when your output format is CSV, for example.

  12. Jamie Hill Says:

    I agree with johan-s. Presenters become really useful when dealing with multiple models at the same time by giving the presenter a save method that handle all of the associations etc. before the save.

  13. Scott Says:

    I was just thinking about the proper placement of business logic in helpers earlier this week.

    I had a similar problem where I and put the logic into the helper, but I thought it was odd to have HTML and business logic both in the helper. So, I was considering putting it in the model, but hesitated for similar reasons.

    I'm thinking now of just having two helpers--a helper for UI and a helper for view logic--which is sort of what the Presenter does.

    I'll be interested to hear about what you decide to do.

  14. RG Says:

    I worked on a Java project where the prior team essentially built a bunch of Presenters to wrap Java objects. Major problems 2 years out:

    • as Morgan intuited, you add a ton of complexity for minimal gain. When something breaks, you have to go traipsing all over the place to fix it. (BTW the natural feature creep here is that there will be a new hierarchy of Presenters.) One thing I really like about Rails is that in general I can understand a page by looking at its controller and its helper and then I'm done.

    • the Presenter pattern isn't so hot if you're working with semi-technical designers (core expertise in HTML/CSS/Photoshop, but not afraid of PHP-like languages like Erb). In our project, design decisions that I believe are endemic to this pattern (or anti-pattern) actually made the view more brittle. See, front-end designers are NEVER going to modify the Presenter code, where they would have no problem writing simple loops etc. in Erb.

    Also, look at the lovely diagrams in the post. You've basically swapped the Presenter for the Helper (missing from the latter diagram). So you create these problems and end up with the same number of stages in the pipeline. Not to mention, your Rails codebase is now a bastard that needs explaining, unlike standards-compliant code. Finally, you lose the ability to piggyback on future rails upgrades of e.g. Helpers and ActionView.

    Maybe they need this in Java, but IMHO Rails got it right already.

  15. Neil Wilson Says:

    I remember Bertrand Meyer saying to me once (and he probably said it in his books as well): "if you need to use an if you need to use one".

    Abstraction is there to ease understanding. You stick in a new layer only when you get a genuine structural simplification. Just hiding an if statement isn't enough.

    Layers are just such an abstraction. You don't get struck down by lightning because your layers aren't 'pure'.

    You only get struck down if somebody else can't understand your code. :-)

  16. bill.stickers Says:

    in your controller, you will need to check to see if the item is_active? to protect against rogue data being submitted, and this is properly done in the model.

  17. John Says:

    "Not to mention, your Rails codebase is now a bastard that needs explaining, unlike standards-compliant code. "

    To be fair, the core team have expressed the possibility of including presenters as part of the framework in the future. Good ideas should not be held back only because it hasn't officially been implemented.

  18. heidmotron Says:

    Is the 'is_active?' entirely view logic? Wouldn't the business want to know what cart item was actually active?

    The abstraction seems to be tightly coupled with HTML(maybe that's the point of the presenter). But if you have an XML view would you have to duplicate the logic to convey the same meaning to the user? It seems like the logic captured whether the variation is nil or the product of a variation was deleted would be best to reside in the is_active method of the cart item.

    Also the abstraction is less intention revealing like product_link, where it may or may not return a link but the method is saying it'll return a link.

  19. ynw Says:

    It would make everything more testable, but in every day environment Html/Css guys would be afraid of it; helpers work very well.

  20. Nick Kallen Says:

    I'm big on the presenter pattern, specifically for testability. I think the key to making the presenter pattern successful in Rails is to never call any standard Rails helpers (or markaby, etc.) in a presenter; only call custom helpers. An example is the following:

    def create_or_destroy_friendship_link(friend)
      if current_user.befriends?(friend)
        destroy_friendship_link(friend)
      else
        create_friendship_link(friend)
      end
    end
    

    Notice that I'm not calling link_to directly. I can then Test this code by:

    createor_destroyfriendship(atruefriend).should == destroyfriendshiplink(atruefriend)

    The alternative way of testing this often involves regexps, yuck!

    This is the "presenter" pattern implemented without an actual class. The class comes when things get more complex.

  21. Chris Anderson Says:

    On Grabb.it we use the presenter pattern to shield our designer-selves from the possibility of triggering horrible queries in the views. Each track has depths and depths of metadata, and rebuilding its presentation can take too long for a request. So we standardized on a JSON layer that is used both in our views and in our Javascript application. Maybe someday we'll be storing it in CouchDB but for now we're just happy to have a clean caching solution. Its nice to have sweep-rebuild caches, but nicer still if you can modify them in place and avoid the whole rebuild cycle.

    Another upshot of keeping the data model for the views in a JSON-style hash of strings is that we can easily reuse our templates for both client and server side view generation. This means google and noscript users see the same interface normal browsers see, and I do only a little work to translate the Javascript templates to erb.

  22. Mark├║s Says:

    I prefer the "approach posted by Jay Fields":http://blog.jayfields.com/2007/03/rails-presenter-pattern.html about the use of the presenter pattern. The main idea he proposes is to merge more than one model into the presenter and use it in the view as a single piece for the sake of clarity and testing reliability rather than simply substitute model methods or helpers. Methods like is_active? are clearly part of the model.

  23. Christian Romney Says:

    One thing no one has mentioned is that presenters are often used so that the views don't modify the objects they're displaying. In this sense, Presenters give you a read-only state bag which the view merely displays in some representation.

    One thing you don't want to see in your views is @cart_item.save, for instance. Presenters prevent this nonsense by exposing immutable objects.

    Other things like sorting, mapping, and reducing go on behind the covers here as well. How many times have we seen stuff like this in the view:

    @tags.collect {|t| t.name}.sort.each do |tag| <%= content_tag('h3', tag) %> end

Sorry, comments are closed for this article.