Skip to content

Rails STI support in Ember Application Serializer

Erik Hetzner edited this page Apr 18, 2018 · 2 revisions

Rails STI support in Ember (Application Serializer)

This page exists to provide historical context and motivation for the complex code in the Application Serializer on the client side.

Background

Right after we switched over to Ember in like March of 2014, we realized that Ember Data didn't do STI out of the box (our senior engineer was flabbergasted). The Ember Data store deals with polymorphism in a different way than Rails does, especially with the way we use ActiveModelSerializers. The expectations of Ember's ActiveModelSerializers implementation are a bit at odds with how we (at least thought at the time) needed to serialize down type information for those Tasks.

It's useful to take a look at https://github.com/ember-data/active-model-adapter. The adapter takes the rails response as an input and spits out the JSON API document as its output, and then that JSON API document gets pushed into the store. In considering what needs to happen it's helpful mentally to split those two steps apart. Almost all of what we do in our custom code it working with/around the existing adapter's expectations to get a JSON API document that the store expects.

For historical flavor, https://github.com/Bestra/ember-data-sti-guide is something I wrote with Ember Data 1.8ish stuff in mind after we did our initial implementation (if you google for "ember rails sti" it's sadly the #1 result). That document is outdated in terms of our current implementation but it still reflects our motivation at the time. To summarize the main nasty case: There are 2 places in the app (the paper.task.index route and the paper.task.version route where we say something like store.findRecord('task', 5) but we don't actually know what type of task we're going to put in the store. Ember data doesn't expect that, so we have to implement some work arounds.

There's an older RFC for adding polymorphic find support to the ember store, but it hasn't seen much activity: https://github.com/emberjs/rfcs/pull/67

Ember Data 2.0 changed the serializer api significantly. We made our existing implementation a little more general, but a little harder to follow code-wise. It deals with all our payloads in the same way rather than looking for tasks specifically.

Implementation Details

Why do this in the Application Serializer if it just deals with Tasks?

Sideloading! We first tried just doing this munging in the Task Serializer, but the problem we ran into was that Tasks would be sideloaded in the payloads that were dealt with by other serializers, and those Tasks would fail to munge correctly. We moved the munging code into the Application Serializer to make sure it would happen no matter what.

Is this really necessary?

The complexity of the implementation stems from its generality, as well as from the layer of the adapter/serializer chain in which we've implemented it. We very well might be able to remove our nastiest cases (meaning do some workarounds to avoid store.findRecord('task', 1). We might be able to shift some complexity to the Rails side, or in general change how we serialize down our Task payloads. I think our many Task model types on the ember side are here to stay for now, if only because we've attached different methods to the subclasses.

Clone this wiki locally