Rails + REST: views nightmare
Rails is rapidly getting more and more popular as a comfortable platform for REST services. And it really is. We do Rails in this way for quite a long time already. There is however a real problem: JSON views are unmanageable!
At first it may look like everything’s just fine. All you need is
.to_json or RABL is some particular cases. But then things go wild. And you start switching JSON Builders one after another.
Let’s say you have a banking service. That’s like 30 models. Each has extended CRUD endpoint (extensions are maybe 3 or 4 methods per endpoint). Each model has like 10 or 12 fields which are quite common to be large strings. And off course all that stuff is insanely linked up to like 4 or 5 levels of
The another thing to remember is that in real life your JSON entities are not just dumps of your ActiveRecord attributes. Two very common things are conditions (whether an attribute should appear) and custom methods.
The problem is that consumer often wants unique set of fields for EVERY method among EVERY endpoint. Set of relations’ fields can differ too!
Imagine that, you have
Comment CRUDs. Every CRUD has 5 methods and potentially different field sets to otput. That’s already 10. Also when you otput
Post you might want to inline serialized
Comment relation. This will give you even more potential field sets for
Comment. Imagine potential number of field sets for deeply nested entity. And every different field set has its own conditions and custom methods. We gonna die, aren’t we?
Life with pain
The first thing we came with to was to leave the RABL alone. It looks fun and effective at first but you simply can not do anything complex and custom enough with that. And in real life RABL did not really go far away from basic
.to_json. It’s a pitty but when it comes to serialization straight declaration is the best declaration. And RABL was built upon magic.
We’ve tried a lot of different builders and finally stopped with Jbuilder. It’s both straightforward and allows boilerplate-less.
But the nightmare hasn’t gone. What do you do to keep your view DRY? Use partials, right. In a very short term that gave us 10-15 partials for each model. That’s 30 models * 15 partials = 450 files at your
app/views folder. Unmanageable. Again.
The Presenter pattern
Another approach to solve this problem with better organization is the Presenter pattern. Since our views are just ruby code it’s a good step forward to fulfill it with OOP.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
So we reduced the number of files and grouped similar sets into the one method with parameters. It’s 1-1 number of models presenters declaring sets of fields. Time to refactor around with Draper gem. With help of Draper, our code turns into:
1 2 3 4 5 6 7 8 9 10 11 12 13
But now again we stuck into the DRY problem that was initially solved by JSON builders. It should be noted that we don’t really need to work with hashes internally. We can build our response from a set of strings using Jbuilder internally at our presenters.
At the moment I write this Jbuilder does not allow us to inject raw JSON string into response. There is another approach to get the required result though. There is a nice fork (pull request was approved so this is expected to be supported by Jbuilder very soon).
With help of this fork we can turn our presenter into following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
So here is the final look:
This strategy is expensive and useless for small services. But as soon as you start operating massive entities and large amounts of custom methods – this is the way. It makes your REST providers exact (serving minimum required amount fields), DRY and supportable.
Keep on reading
You can find some real-life experience within this post: http://blog.alerticus.ru/post/20183094648/rails-rest-avoiding-the-views-nightmare-practice. If you think it’s worth trying, keep on reading :).