Skip to content

Instantly share code, notes, and snippets.

@foxnewsnetwork
Last active August 29, 2015 14:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save foxnewsnetwork/24e4e228d09cebc0b0cb to your computer and use it in GitHub Desktop.
Save foxnewsnetwork/24e4e228d09cebc0b0cb to your computer and use it in GitHub Desktop.
Complex Front End Data Models in Emberjs

The Motivation

EmberJS is a full-featured front-end javascript framework for building (as they put it) ambitious apps. Being ambitious, I wanted to build an app that had data models that were queryable like those of SQL, realtime like it was on Websockets, I wanted to do it without building my own customized REST-over-websockets-on-Elixir server stack. Why would I want to do this? Mostly because so many cuts on the server-side reduces devops costs (to 0) and allows me to piggyback on the extremely well-staffed architecture that runs places like Github and Google (so I don't have to ever worry computer-level details like disk IO rate, server RAM, database backup, SSL, geographic distribution, etc.; all of that I would get for free).

TL;DR: so I can build a globally scalable app that I can maintain just by myself (because there is no maintainence)

The Problem

Using Javascript to do heavy client-side data-management means I am either condemned to Calback Hell or (since it's 2015), Promise Monad Hell (yes, promises are a Monad... and an arrow). And you immediately run into something like this:

MasterTruck = DS.Model.extend
  railTruckId: DS.attr "string"
  fireTruckId: DS.attr "string"
  targetDockPosition: Ember.computed.alias "fireTruck.dockPosition"
  
FireTruck = DS.Model.extend
  ... # firebase attributes
  dockId: DS.attr "string"
  dockPosition: Ember.computed.alias "dock.firePosition"

MasterDock = DS.Model.extend
  railDockId: DS.attr "string"
  fireDockId: DS.attr "string"
  firePosition: Ember.computed.alias "fireDock.position"

RailDock = DS.Model.extend
  position: DS.attr "coordinates"

Because we're using 4 models across 2 different external vendor-specific database systems, there is no possibility of embedding one model's data into another; you'll have to use promises and or callbacks (or wait until es7 async/await becomes real). In non-Ember specific code, this isn't a problem, I could just use promise.then chains and write out what I want. But, when I need to do some render-related calculations in my components, this becomes a massive stumbling block.

The Ember render stack expects pure values, not promises (Ember's routes actually wait for its model to resolve before rendering its template and loading its controller), and whatever calculation I want to do also expects pure values (at least if I'm doing good practices). But my models necessarily must have promises embedded inside their computed properties, so what do I do?

The solution

After looking around and asking, I decided to roll my own solution library. It's available here:

github.com/foxnewsnetwork/ember-data-complex

But Here's how I wanted to solve this problem:

MasterTruck = DSC.ModelComplex.extend
  ... # rest same as before
  firePromise: DSC.promiseTo "fire/truck", foreignKey: "fireTruckId", foreignField: "fireTruck"

FireTruck = DS.Model.extend
  ... # same as above
  dockPromise: DSC.promiseTo "master/dock", foreignKey: "dockId", foreignField: "dock"
  
MasterDock = DS.Model.extend
  ... # same as above
  firePromise: DSC.promiseTo "fire/dock", foreignKey: "fireDockId", foreignField: "fireDock"

Here, DSC.promiseTo is computed property which always resolves to a promise. When the promise resolves, it automatically sets it to the field specified in foreignField. Being explicit like this allows the programmer to have access to both the easily serializeable blahId field, the resolving promise, as well as the eventual value. This has the advantage that you are now able to play well with Ember's observer pattern, at the same time, be able to handle synchronization.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment