View Models, Form Objects, Presenters, and Helpers Oh My!
/This is the beginning of a series of blog posts on the different entities we at AppFolio use to manage logic in our server-rendered views. Now, much like the rest of the Rails community we too are moving towards more client-rendered views and a more client-server architecture but the majority of pages are still rendered server-side, and that will be the case for many years going forward. In such a setting it therefore still makes sense to discuss how we want to organize the code that renders these server-side views.
In this series we will be doing a deep dive into each of the concepts individually, but to start out I thought it would be good to give an overview of each concept and what we at AppFolio mean by them. There doesn't seem to be a consensus in the Rails community about what terms like “Presenter” mean and where the boundaries of responsibility lie.
I will by no means claim that what we’ve done will work for everyone - but it works well for us, and who knows - it may do the trick for you as well!
Key Concepts And Where To Find Them
View Models
At AppFolio a view model is the single object of interaction for the view. It is responsible for implementing the interface (data requirements) of a particular view template or partial.
I personally find a few things appealing about this. As someone who is still relatively new at AppFolio, a well-named method encapsulating complex logic to, say, decide whether or not a particular message is shown really helps me get up to speed quickly on the reason why a message should or should not be shown.
<% if !Company.has_enabled_this_feature? && (this_condition || that_condition) && one_more_condition %> <p>I’m an important message that will help drive adoption but only in certain contexts</p> <% end %>
Is far less helpful than
<% if view_model.display_feature_marketing_messaging? %> <p>I’m an important message that will help drive adoption but only in certain contexts</p> <% end %>
Particularly given that when first working with said feature I’m more likely to know whether my changes should or should not be part of the marketing messaging than I am to know exactly which conditions we’re currently relying on for displaying the messaging. And if I do need that deep dive that logic is now isolated from the html noise in a PORO (Plain Old Ruby Object) for my casual perusal.
Not to mention the fact that if there need to be changes made to that logic I get to write nice little unit tests instead of controller tests. In addition to unit tests being faster than controller tests, unit tests also enable me to understand things more quickly, since the expected setup and output aren’t masked behind making get requests and parsing response bodies.
Form Objects
Form objects serve much the same purpose as view models but they specifically back an HTML form, and again in much the same way they implement the interface (data requirements) of a particular HTML form.
Why, then, would I not include them in the view models section? Well, form objects have some added responsibilities since they handle things like validations after the form has been submitted. Because of these extra responsibilities you will end up instantiating form objects in, for an instance, the update action of a controller, and as long as everything is valid that object will no longer be used to render the subsequent view (unless you want to render the same edit view even upon a successful form submission)
They further distinguish themselves from standard view models in that they “quack” like an ActiveRecord object in the sense that they have validations on the form fields (they include ActiveModel::Validations
and other ActiveModel modules.) Furthermore, depending on the complexity of saving the actual form it may extend the default save behavior of the related objects.
Of the concepts discussed here form objects are the ones I’ve worked with least, but their value is already apparent from the few times I’ve worked with them. Because few forms represent only one ActiveRecord object, this separates business logic from, writing HTML, and encourages slim controllers.
Presenters
Presenter is by far the term that is most prevalent, and it can often include some of the responsibilities we’ve given to the view models, but for us a presenter wraps a particular ActiveRecord model or business concept. It provides methods that transform and format data for consumption by a view or another consumer (such as an API.) Since this blog post series is all about views we’ll just be talking about the view case.
I think at this point, a small example is in order. Imagine if you will that we have in our app the idea of a PhoneNumber
- I know, radical right? But everywhere that we show a phone number we want it to be a TelLink to also have a button for sending a text message.
The exact styling of the number may change depending on context, so a full partial/view model pairing isn’t needed. Instead we’ll create a phone number presenter that takes in a phone number object and exposes a set of methods that return the various components that we want to have, namely an instance of a TelLink
and an instance of the SendTextButton
.
The great things about this is that we can instantiate this presenter in any view models that help render a view containing phone numbers, allowing the behavior to be consistent across views regardless of the context.
Helpers
A helper is a “functional” method (meaning it relies only on its inputs and not on any internal state stored in the classes it is included in) that provides easy access to commonly used logic. Rails of course provides many of these (such as link_to
) but there are some basic formatting methods that also make sense as helpers, such as a method that takes in a value and provides a default value if the value is falsey.
def value_with_default(value)
if value
value
else
‘--’
end
end
What we’ve found is that many of the things we had put in helpers would actually make sense in a presenter, and since they were in helpers they ended up being included in other helpers that were then included in other helpers, leading to a big ‘ol ball of spaghetti that makes cleanup a lot more difficult.
In general, I’ve found that biasing towards trying to put something in a presenter first and then seeing if that feels off yields the best results, and it certainly leads to more OO design. After all, there are few things that are so ubiquitous that they aren’t tied to some well-defined business object or concept.
Looking Forward
Hopefully this overview has helped you get a basic idea of how we at AppFolio talk about and use view models, form objects, presenters, and helpers, as well as the value that adopting this sort of structure offers a development team. Over the next few months I’ll be releasing in-depth dives for each of those concepts focusing on real use cases and highlighting the value that view models can have to you, the developer.
In the meantime if you have any questions or feedback, feel free to reach out to me at mischa.lewis-norelle@appfolio.com or leave a comment!