Sunday, October 14, 2007

Rails: Rise, Fall, and Potential Rebirth of the Presenter Pattern

The default architecture for Ruby on Rails, Model View Controller, can begin to break down as Controllers become bloated and logic begins to creep into view templates.

The Presenter pattern addresses these problems in concert by creating a class representation of the state of the view...
And on it goes, the description for my RailsConf Europe 2007 talk. Unfortunately, this talk was the worst of my career. Pretty much everything went poorly. No one came in to make sure the microphone was working (it wasn't). They ran out of water bottles. I had a fever (103 degrees Fahrenheit, when checked later that evening). But, worst of all, I did a poor job of getting my message across. This post is an attempt to clarify what wasn't expressed well in Berlin.

This post assumes you've read the blog entry: Presenter Pattern

I first applied the presenter pattern to a Rails codebase in June of 2006. I had a complicated view that displayed several models aggregated into a single, readonly report. I was working on the code for a while and couldn't find an easy way to test that the view displayed what I was looking for. All of my models were being created and aggregated in the controller. There wasn't a single uber model that could have handled the aggregation, it simply didn't make sense to model it that way.

After spending a bit of time trying to make it work I decided that I needed a more testable solution. Instead of creating and aggregating all the data in the controller I created a PORO that assumed those duties. The presenter had clear responsibilities: find and manipulate the data so that the view can display it by simply calling @presenter.output. This solution turned out beautifully; it was easy to test and maintain. That project contained one presenter, the one spot where it made sense.

In November of 2006 I joined a new project. The new project was basically a wizard that collected several bits of data. Unfortunately, the views didn't easily map to the domain model. Some views needed to validate portions of a model and other views needed to validate multiple models. We decided to use presenters to aggregate and adapt the models to fit the views. The situation was different this time though; this time the views needed to accept post data. There were several implications that came with this choice. The presenters now needed to handle synchronization with the models instead of simply pulling data from the models. The presenters also took the responsibility of validating where it made sense. This experience led to another blog entry: Another Rails Presenter Example.

Presenters worked well on this project; however, the responsibilities of presenters had grown. At the time I didn't realize it, but they were only able to cope with the additional responsibilities because the application followed such a strict convention: Always post and move forward or backward. Like I previously mentioned, this was an application that was basically a wizard. Every page posted, checked for valid data and moved forward or backward. Because the behavior was so limited we were able to easily metaprogram that behavior into the presenters. As a result, we rarely had to do anything interesting in the presenter other than write the formatting, validation, and synchronization code. Again the pattern worked well, so I decided it was time to put out something describing the pattern. The Presenter Pattern blog entry was a result of the combined successes of applying the pattern.

At this point David and Jamis had added a few presenters to Highrise and Jamis had written several blog entries with a very similar idea. It seemed like a great time to submit the presenter pattern as a talk for RailsConf Europe 2007.

Then in April of 2007 I moved on to another project. This project was a more typical Rails application. It didn't follow the wizard pattern. It's views where both readonly and read/write. It often needed to validate something and do interesting things based on those validations. There was no easy convention for the views to follow. Presenters were being used when I joined the project, but they weren't proving to be a good fit. We struggled to find a canonical example within our codebase. We also struggled to find which responsibilities were a good fit and which weren't.

Then, Yogi Kulkarni joined the project. Yogi pointed out that our presenters were responsible for far too much. Yogi wasn't the first person to say this, but he was the first person to say what objects we were missing: Services (Domain Driven Design). We started adding services to the application and the presenters became slimmer and more easily testable. The suggested implementation from my previous blog entry had failed; however, we were able to remove the service related responsibilities and make use of the other benefits of using presenters. Ultimately, the svelte presenters provided a better solution.

That's where my RailsConf talk really fell down. I wasn't able to express that Presenters weren't a silver bullet, but they did seem to work well in various forms.

Several people came up to me after the presentation and said "I'm using presenters to... do you think that's a good idea?" My answer was yes to each query, and those people had found valid ways to use presenters to make their life easier. The key was figuring out how presenters helped you and limiting their responsibilities to those tasks.

Following my experience on my last project I was hesitant to look for a general solution similar to presenters. It's still a pain to test views and controllers, but adding another layer that also felt painful didn't seem like a good solution. Again, I think presenters have their place, but I wasn't sure that a general solution was available.

Then, at RailsConf Europe, Marcel Molina Jr showed me a version he was working on. He's currently using presenters in a non-intrusive way that allows you to use vanilla Rails by default, and add presenters where necessary. Marcels solution looks promising, but it's not publicly available. That's actually a good thing since he's maturing it a bit before releasing it.

I'm looking forward to Marcel's results, but in the mean time I think everyone can benefit from the experiences of the people who have begun blazing the presenter trail. In the blog entry Presenter Links I provide links to entries by Jamis and Courtney. Those are the more popular links, but several other people have written about their own experiences.

The conclusion I failed to express is this: If you are feeling pain testing views or controllers, you aren't alone. I haven't seen the silver bullet solution, but I have seen several solutions that worked very well for specific systems. I believe the current best solution is to read up on the experiences of others and see if anyone has solved a situation similar to yours; if so, try out their suggested implementation. If you find yourself with a unique problem, look for why the existing solutions worked well for their respective problems. Take what applies to you and form your own solution, then write about your experiences so that other people can benefit from your experience.

3 comments:

  1. Anonymous5:07 PM

    I haven't seen the silver bullet solution, but I have seen several solutions that worked very well for specific systems.

    That's probably because there are no silver bullets ;).

    ReplyDelete
  2. Anonymous10:00 AM

    Actually, Jay, I thought you did get that message across just fine. :)

    ReplyDelete
  3. Jay, I've been using the Presenter class from the restfully_yours plugin lately with good success. So far its allowed me to move some of my -- more complex than normal -- to_xml, to_atom and other methods from the models to a place that feels better. Its not only easier to test, but easier to share behavior between similar classes than when using views.

    ReplyDelete

Note: Only a member of this blog may post a comment.