Consequently, the time available for me to blog has rapidly dwindled. I set a goal for myself to write one blog post a week as a way to motivate myself to keep learning code and exploring technology. What I’ve learned, though, is that after a program like Flatiron, where you’re immersed in code and laser focused on rapidly building an entirely new skill set, I’ve hit my stride with code in that its become a familiar extension of myself, like writing or reading. This means I can keep learning about code while living a more balanced lifestyle than I did at Flatiron; I can’t imagine that the countless hours spent writing, reading and talking about code in school is particularly sustainable in the long run.
But now back to the code. I recently implemented the Presenter pattern in my team’s Rails application. For those not familiar with the Presenter pattern, its a way to keep your view and controller logic as clean as possible by having a Ruby class serve as the interface between your model and the view/controller. Another way to think of a Presenter in Rails is as a serializer for your Views. And much like how ActiveModel::Serializers enable you to build a clean API and encapsulate API logic within methods that are easily testable, Presenters keep your model-specific view logic in one class that you can test like you would any other Rails model.
Imagine you have an Article class that represents a newspaper article. Articles each have associated multimedia elements, which can vary from an image, video or slideshow. For the sake of this post, an article is a stripped down model that just stores a serialized hash to represent the associated multimedia assets.
1 2 3
The multimedia attribute looks something like this when serialized.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
In my views and controllers, I need to be able to access the urls and dimensions of each multimedia asset to render a preview of the asset. One option to get these attributes is to do a whole bunch of type checking within my views and access the url of an image as follows:
Rather than have the logic in the controller or view, the Presenter pattern provides a Ruby class that wraps a Rails model in order to create a simple API to the underlying object. Rather than going through the hash in my view, what I want to be able to do is just call #url or #caption on my article instance and get back the necessary information. I can add methods to my ArticlePresenter class to semantically access multimedia urls in my controllers and views rather than constantly checking a key-value pair in a deep nested hash.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
This way, in my controller I can wrap each article in a collection in an ArticlePresenter instance, so that in my views I just need to call @article.small_image_url or @article.large_video_url to get what I need rather than checking the multimedia hash. Both ways get the same result, but using a Presenter provides a testable object-oriented interface for view-specific logic relevant to a Rails model.
And like ActiveModel::Serializers, I can set default values for nil return without having to change the underlying data. In this example, if an article multimedia item doesn’t have a caption, instead of returning nil to the view, I can instead return a string, “No caption”. This helps tailor your model instance to specific views without adding unwiedly logic to your ERB/HAML templates.
1 2 3 4 5
One question I had when exploring the Presenter pattern was how it differed from plain old Rails helpers. I think Presenters are useful when you have logic specific to your models that you need encapsulated, whereas Rails view helpers are intended for more generic logic that is model agnostic, like converting dates or measurements. Of course, you could probably use view helpers to accomplish the same function as Presenters, but having a one-to-one relationship between a Presenter and its model feels cleaner to me.
Here are a few links that I found helpful while implementing the Presenter pattern.