Using I18n and Draper to Render Database Attributes
TL;DR;: Check out my additions to ApplicationDecorator in this gist.
When my models have an attribute that matters to the code (like Admin#role or User#status),
I like to store the value as a string that makes sense as an identifier.
For example, User#status might be ‘active’ or ‘awaiting_approval’.
However, when it comes time to render the admin’s role or the users status in the view,
we want to show ‘Awaiting approval’ instead of ‘awaiting_approval’.
Another example of this sort of thing is the #type attribute for STI.
Ok, this isn’t too hard, we can just use #humanize. But, here’s what happens:
Ok, let’s be fair. All of these solutions are actually quite fine. In most cases Ya Ain’t Gonna Need anything more complicated. The helper version handles most situations just fine.
However, after a bunch of this I tend to end up with a bunch of methods in my model that seem to be somewhat presentation related, and/or methods in my helper that seem like they belong to an object and not in the “global” view namespace.
Enter decorators
A decorator (or presenter) is an object that holds the presentation logic for a model, so that the model can stick to the business logic. I’ve been using a great gem called Draper. I won’t go into too much detail about how to use Draper (check out the Github readme or Railscast).
Here’s how you would implement the above pattern with Draper:
Then this is our view:
Bonus
Now:
My Abstractions
And now the reason for this post. I find that I use this pattern frequently, so I generalized it to ApplicationDecorator.
It adds a class method ApplicationDecorator.humanizes that can be used in each decorator to define attributes that need automatic humanization.
The full source can be found here: https://gist.github.com/1338134 (Show inline)
Here’s how you would use it:
And in the view:
To Conclude
I like this because each layer is really simple and really focuses on only what it needs to.
- The view doesn’t have to know that that data is not user-friendly.
- The model isn’t polluted with methods designed for the view.
- There isn’t much complexity or black-magic to make this abstraction simple.
If this pattern works out in my current project I will probably pull this out into a gem. Would anyone else find this useful? If I do I’ll be looking for name suggestions…
