Sunday, February 18, 2007

Rails: Presenters - An additional layer alternative.

The first time I wrote about Presenters was back in September. The title of the article contains a question mark for a reason, at the time it was a brand new idea that I wanted to share, but not endorse as "a good thing".

Time passed, a new project started for me, and Jamis wrote Skinny Controller, Fat Model, Moving associated creations to the model, and a blog entry that mentioned (in the comments) that he and David have been thinking about the pattern.

An important thing to note about Presenter is that it adds an additional layer; therefore, more complexity. This is definitely a trade off worth considering, and the reason that I believe that Skinny Controller, Fat model is the way to go if you can get away with it. However, when you begin working with multiple models on one view, I do believe it's time to give a presenter a chance. Which is why I created an entry with another presenter example in response to Jamis' Moving associated creations to the model entry.

I closed the Another Rails Presenter Example entry with the following paragraph
Using Presenters has additional advantages such as their ability to easily integrate with ActiveRecord validations, separation of view behavior from models (such as formatting a phone number), and allowing you to put validation error messages somewhere other than in a model. I'll address each of these scenarios in upcoming blog entries.
Since I wrote that entry I've touched on a few of these topics, but never brought it all together.

So, taking the paragraph one sentence at a time, here's my explaination.
Using Presenters has additional advantages such as their ability to easily integrate with ActiveRecord validations
This statement was somewhat misleading. Presenters do easily integrate with ActiveRecord::Base models. They can easily aggregate errors from multiple models and display those errors from the presenters errors collection. However, this is currently possible if you include Validatable, and ActiveRecord::Base validations could simply add this behavior and presenters would provide no superior solution. There's a discussion around whether models aggregating errors is a good thing, but that's an discussion for another entry.
[Presenters allow] separation of view behavior from models (such as formatting a phone number)
This is quite true, and one of my favorite aspects of using presenters. Consider an Order class that contains a total attribute. The total is stored in the database as a Number (Oracle). The total needs to be displayed to the user (in the view), formatted as currency, but without the unit ($). The easy solution is to put the following code in the view.
number_to_currency(@order.total, :unit => "")
While this does work, it's basically not unit testable. It is functionally testable, but not nearly as neatly as I'd prefer. An alternative solution is to mix ActionView::Helpers::NumberHelper into the model. Then I can unit test my ActiveRecord::Base subclass, but adding view behavior to models can begin to lead you down (or, some may argue, you are already on) the wrong path. Therefore, adding a presenter, an object that represents your view, allows you clearly separate concerns and create more maintainable tests. Since presenters are class versions of your views, including ActionView::Helpers::NumberHelper in a presenter makes perfect sense. And, Presenters should be easy to test without relying on rendering and other framework dependencies.
[Presenters] allow you to put validation error messages somewhere other than in a model.
Putting error messages, that will be used as display text, in a model might or might not bother you. There's an academic argument available, but actual software delivery is what I'm interested in. Unfortunately, pulling errors messages out of models isn't simply an academic issue on my current project. We have requirements that include storing errors messages in a database so they can be manipulated by business users instead of programmers. There are few things in developing software that I believe are black and white, but empowering subject matter experts is one thing that I see little merit in arguing against. So, this wasn't just a requirement, but something I believed was the right idea.

Since we chose to use presenters, we were able to specify keys in our validations. Those keys were used by the presenters to pull the appropriate errors messages from the database. This solution was simple, and more importantly, easily testable.

Shortly after I wrote Another Rails Presenter Example, Jay D. left a comment on the original Presenter entry.
...in the example you gave I would have moved the code to the model.

"However, a better example would have been if the necessary data is spread across various models."

I still don't see why it wouldn't work in the model...

My controllers are clean like the one in your example and I can easily test the data aggregation/manipulation in my unit tests. Why would this be a bad thing?
By the time this comment came across, I had been using Presenters heavily (1 per view and often 1 per partial) on my current project. I still don't believe that they are required; however, I do now believe that they can be a valuable pattern when applied correctly. I responded to Jay D. as a comment; however, I believe Jay D's question is common enough that my answer is worth repeating in this entry.
The classic example for using a Presenter is a summary page. For example, if you buy books from Amazon, the final page has information about each book that you bought, tax information, shipping information, billing information, user account information, etc. In that scenario, I'd prefer to aggregate all that data in a Presenter.

Presenters offer other opportunities for separation of concerns that models do not. For example, if you want to validate that the user entered two matching passwords or email addresses you must add 'virtual' attributes to your models. The same is true if you would like to create an acceptance attribute (to use validates_acceptance_of). You could argue that since these attributes are not persisted to the database that they should not live in the model.
Presenters are no more a silver bullet than any other pattern provided in the past; however, they can be helpful when used correctly.
Post a Comment