Friday, March 1, 2013

EmberJS, Notes and Gotchas

JSON Interface
Description of the expected REST interface, adapted from http://stackoverflow.com/questions/14922623/what-is-the-complete-list-of-expected-json-responses-for-ds-restadapter :

ContextServerURLMethodReq. DataResp. Data
Get list of all/usersGET{"users":[{...},{...}]}
Get one/users/123GET{"user":{...}}
Create/usersPOST{"user":{...}}{"user":{...}}
Update/users/123PUT{"user":{...}}{"user":{...}}
Delete/users/123DELETEN/Anull
Create in bulk/usersPOST{"users":[{...},{...}]}{"users":[{...},{...}]}
Update in bulk/users/bulkPUT{"users":[{...},{...}]}{"users":[{...},{...}]}
Delete in bulk/users/bulkDELETE{"users":[1,2]}{"users":[1,2]}

This is EmberJS's documentation on this:
http://emberjs.com/guides/models/the-rest-adapter/#toc_the-rest-adapter

Ember Gotchas!
  • Ember tolerates no unmapped fields in your JSON results! Presence of these will result in an Ember.assert() error, which in Firefox produced no logging whatsoever, just a silent failure!
    • Example: if your data looks like this {"users":[{"name":"bob","id":3}], "count":1}
      and Ember does not know about the 'count' field, JSON response processing will abort
  • Ember transforms camel-cased model field names into underscored names when emitting JSON, and expects underscored names in retreivals:
    • e.g. model field 'isCompleted' will be sent in JSON updates as 'is_completed' - and must be sent as 'is_completed' as well
    • WORKAROUND: you must explicitly map the attribute in the RESTAdapter - see the EmberJS documentation here
    • or see the related Question below for a workaround
Questions
window.Todos.TodoRESTAdapter = DS.RESTSerializer.extend({
   keyForAttributeName: function(type, name) {
     return name;
   },

   keyForBelongsTo: function(type, name) {
     var key = this.keyForAttributeName(type, name);

     if (this.embeddedType(type, name)) {
       return key;
     }

     return key + "Id";
   },

   keyForHasMany: function(type, name) {
     var key = this.keyForAttributeName(type, name);

     if (this.embeddedType(type, name)) {
       return key;
     }

     return this.singularize(key) + "Ids";
   }
});
and then use this in your Stores:
Todos.Store = DS.Store.extend({
 revision: 11,
    adapter: DS.RESTAdapter.create({ 
     bulkCommit: false, 
     namespace: "todo/api"
     serializer: Todos.TodoRESTAdapter
    })
});
  • Q: How to do the analagous replacement of underscore() when serializer is serializing back to REST?
    • A: Here is how serializing to REST gets done:
      • base class DS.Serializer.serialze() is invoked
      • calls DS.Serializer().addAttributes() which loops through all attributes
      • calls DS.Serializer().addAttribute() which can be overridden
        • calls _keyForAttributeName()
          • eventually calls keyForAttributeName() in the Serializer
          • this is where we override
  • Q: how should I represent nested data elements?

Execution of find()
  • findAll: function(store, type, since)
    • contains a 'success': callback - set breakpoint here
    • calls didFindAll()
      • uses a Loader, acquired as DS.loaderFor(store), and a Serializer
      • invokes serializer.extractMany(loader, payload, type)
        • where payload is your data! and type = model name
        • stepping into this puts you into a wrapper - step again into the 'apply()'
        • first parses sideloaded data using sideload()
          • see this for description of sideloading:
        • extractRecordRepresentation()
          • getMappingForType()
          • if [ should sideload ]
            • loader.sideload()
          • else
            • loader.load()
              • store.load()
                • this adds a prematerialization entry into a list for later parsing
                • somewhere much deeper in the stack, we get to....
                • DS.RESTSerializer.keyForAttributeName()
                  • which calls Ember.String.decamelize()
                  • this is where underscores are introduced
Pluralizing Strategy
  • executed in method: pluralize()
  • option 1: provide a mapping in definition of the RESTAdapter, as ....

    adapter: DS.RESTAdapter.create({ bulkCommit: false,
plurals: {"security": "securities"} })
  • option 2: defaults to name + "s"
Data Representation Internals, Notes on
  • maintains a 'stringCache' which JSON string field names are looked up against
    • these are given codes like 'st295' where the # is the field uuid

3 comments:

  1. You may want to check out my ember-data-grails project. It eases the process of turning a Grails server into a RESTful backend that Ember.js and Ember-data will interact with into a cinch.

    https://github.com/zachriggle/ember-data-grails

    ReplyDelete
  2. THIS should be on the ember documentation. Very useful, many thanks for posting this.

    ReplyDelete

Superbowl AI

Did you catch that the NFL is using AI  image processing to help settle in-game calls like field position? Seems like some welcome backup fo...