Changes with DS.RESTAdapter?


#1

Hi!

I have the following model

App.User = DS.Model.extend
  name: DS.attr('string')
  lastName: DS.attr('string')
  email: DS.attr('string')
  admin: DS.attr('boolean')
  active: DS.attr('boolean')
  posts: DS.hasMany('post')
  comments: DS.hasMany('comment')

My json returns this:

{
  "id": 1,
  "name": "Eduardo",
  "last_name": "Figarola",
  "email": "myemail@gmail.com",
  "admin": true,
  "active": true,
  "comments": []
}

When I try to do this:

user.get('lastName')

I get a null value. But if I do something like this:

user.get('_data.last_name')

I see the proper value, is there a new naming convention for model attributes and JSON api? I want to highlight that the previous code was working before I updated to Ember.js 1 and Ember Data 1.0.0-beta.1

Thanks for your help!


#2

I just changed the Rails serializer to return the following JSON:

 {
   "id": 1,
   "name": "Eduardo",
   "lastName": "Figarola Mota",
   "email": "lalo.diabulux@gmail.com",
   "admin": true,
   "active": true,
   "comments": []
 }

And it worked, is this the new convention?


#3

Kinda. Before the beta, the documentation was saying that you should use camel case, but if you called it using underscore, it worked too. But now they just handle camel case.


#4

@wycats added some stuff to TRANSITION.md to explain this case, sorta. Basically, you need to subclass the RESTSerializer and modify it to use the old conventions:

ActiveModelSerializer = DS.RESTSerializer.extend
  # Camelize keys and accept _id and _ids for associations.
  normalize: (type, property, hash) ->
    normalized = {}

    for own prop, value of hash
      if prop.substr(-3) is '_id' # belongsTo relationships
        normalizedKey = prop.slice(0, -3)
      else if prop.substr(-4) is '_ids' # hasMany relationship
        normalizedKey = Ember.String.pluralize(prop.slice(0, -4))
      else # regular attribute
        normalizedKey = prop

      normalizedKey = Ember.String.camelize(normalizedKey)
      normalized[normalizedKey] = value

    @_super(type, property, normalized)

  # Underscore keys and use _id and _ids for associations.
  serialize: (record, options) ->
    json = {}

    record.eachAttribute (name) ->
      json[name.underscore()] = record.get(name)

    record.eachRelationship (name, relationship) ->
      if relationship.kind is 'hasMany'
        key = name.singularize().underscore() + '_ids'
        json[key] = record.get(name).mapBy('id')
      else
        key = name.underscore() + '_id'
        json[key] = record.get(name + '.id')

    json.id = record.get('id') if options?.includeId

    json

App = Em.Application.create({ ApplicationSerializer: ActiveModelSerializer })

You also need to do something similar with the RESTAdapter to ensure that your API is called using underscore_names consistently:

ActiveModelAdapter = DS.RESTAdapter.extend
  namespace: {{whatever}}

  # Always use underscore_names in URLs.
  rootForType: (type) ->
    decamelized = Em.String.decamelize(type)
    Em.String.pluralize(decamelized)

App.ApplicationAdapter = ActiveModelAdapter

Note that this code is for the current HEAD, not the shipped release.


#5

@nragaz Thanks for these code snippets!

@wycats @tomdale What are we expecting the default to be here? If we want the default to be sans _id(s) then maybe we should make that the default in active_model_serializers as well, assuming that’s still the preferred way to produce JSON for ember via Ruby/Rails. On a side note, it seems like development on AMS has stagnated waiting for spastorino to finish refactoring on his fork.


#6

A few notes for the other folks new to Ember:

  1. Instead of creating my own “ActiveModelSerializer”, I took @nragaz code and did the following:

    App.ApplicationSerializer = DS.RESTSerializer.extend({ /* etc. */ });

  2. Instead of creating my own ActiveModelAdapter, I merely reopened the DS.RESTAdapter.

The combination of the two steps got me back up and running. Whether or not this is best practice I have to defer to the more experienced on this forum.


#7

The signature for the normalize method changed tonight, so here’s an updated snippet:

ActiveModelSerializer = DS.RESTSerializer.extend
  # Camelize keys and accept _id and _ids for associations.
  normalize: (type, hash, property) ->
    normalized = {}

    for own prop, value of hash
      if prop.substr(-3) is '_id' # belongsTo relationships
        normalizedKey = prop.slice(0, -3)
      else if prop.substr(-4) is '_ids' # hasMany relationship
        normalizedKey = Ember.String.pluralize(prop.slice(0, -4))
      else # regular attribute
        normalizedKey = prop

      normalizedKey = Ember.String.camelize(normalizedKey)
      normalized[normalizedKey] = value

    @_super(type, normalized, property)

  # Underscore keys and use _id and _ids for associations.
  serialize: (record, options) ->
    json = {}

    record.eachAttribute (name) ->
      json[name.underscore()] = record.get(name)

    record.eachRelationship (name, relationship) ->
      if relationship.kind is 'hasMany'
        key = name.singularize().underscore() + '_ids'
        json[key] = record.get(name).mapBy('id')
      else
        key = name.underscore() + '_id'
        json[key] = record.get(name + '.id')

    json.id = record.get('id') if options?.includeId

    json

ActiveModelAdapter = DS.RESTAdapter.extend
  namespace: {{your namespace}}

  rootForType: (type) ->
    decamelized = Em.String.decamelize(type)
    Em.String.pluralize(decamelized)


App = Em.Application.create
  ApplicationAdapter: ActiveModelAdapter
  ApplicationSerializer: ActiveModelSerializer

@jherdman I don’t think it matters which way you go – I like to define the adapter and serializer separately, but that’s just a preference.


#8

@nragaz thanks. You may want to make input to Bradley Priest’s pull request:

Basic DS.ActiveModelSerializer implementation


#9

@yyy Thanks for pointing that out! For my backend I just needed to extend DS.ActiveModelSerializer instead of DS.RESTSerializer - no need to manually configure camelization, etc.