On October 17, 2012, Bryan Helmkamp, founder of Code Climate, wrote a blog post outlining 7 patterns to refactor fat ActiveRecord models in Ruby on Rails. Here at Crush & Lovely, this post is a core reference for all Rails developers on how to separate concerns, write modular, concise and expressive code, and make testing exceedingly simple.
When a model has associated logic or attributes that are used exclusively for surfacing a representation of the model — as HTML in a standard website or as JSON from an API endpoint — a best practice is to avoid storing those calculations or values directly on the model. Storing view-specific attributes on the model can create confusion about what is "truth" (stored in the database) and what is purely representational. View Objects act as a kind of adapter between the truth and the representation of the truth.
For example, the truth of an imagined inventory item is that the price attribute stored in the database is 599 cents, but the representation for the product page may be a mutation of the truth, such as $5.99. It would be inappropriate to store the representational data as a secondary price attribute on the model. It would be even worse to inject the formatting logic into the template.
What View Objects do is "dress up" the data, by way of transforming, adding or removing data properties, returning a new object for use in the presentation layer. This approach creates a nice home for our presentation-specific logic and attributes, keeping it removed from the model truth.
I also want to mention that there is some conflict about what to call this pattern. Helmkamp addresses this in his post, and it's a common topic of discussion here at Crush & Lovely. The term "View Objects" is not the preferred term amongst our engineers, primarily because this name obfuscates the versatility of the pattern, since "view" is the most common term for HTML. This pattern can be used for an API response, for passing data into a third-party service, or for any other reason. "Presenter" is the favored term at Crush, primarily because it aptly describes the function of the pattern: the data is presented for use in a response, regardless of what form the response comes in.
For example, at the end of the year, a teacher prints out report cards for each student. Among other information, the report card shows the student's average grade, whether they are passing or not, and the phone number of the student.
The script to generate the report cards finds each student and their associated assignments for the year, producing a "truthful" representation of the object like this:
The markup for the report card PDF, in keeping with the dogma of "stupid" views, is ignorant of the formatting of the data:
Presenting the student object for use in the view becomes very easy when we throw it into a View Object and in turn throw that View Object into the HTML. Here's an example of what the View Object could contain to dress our student model up for use in the HTML:
And then all we need to do is present the student data and pass it into the view for rendering:
It should be noted here that there are great opportunities here to extract commonly used View Object methods, such as formatting a phone number. You could pull these into a module of helpers that can be used in the View Object, or you could look at composition opportunities, breaking down your View Objects into more modular tools. The opportunities for organization when using View Objects are open and flexible, and each engineer should look at her or his own style and application for best practices.
Unit testing for these mutations is pleasantly straight-forward, since all you're doing is passing in one object or array and expecting another. Thusly, you can just test for the right properties and values after the data has been presented.
In the next post, we'll take a look at Policy Objects, which provides a great tool for encapsulating business logic.