Problem
An embedded object in a JSON payload, that is a result of using has_one :XXXX, embed: :objects with an instance of ActiveModel:Serializers, is not supported yet in the DS.EmbeddedRecordsMixin.
Using Mongoid w/ Rails the ActiveModel::Serializer allows embedded objects via has_many and has_one relationships. Currently Ember Data’s ActiveModelSerializer has support for embedded hasMany relationships using methods: extractSingle, extractArray and serialize.
However, a payload with a format that includes embedded objects using a belongsTo relationship with a payload created using has_one is not supported by the DS.ActiveModelSerializer.
For example: If a server stores a document (e.g. mongodb) with an embedded object, using has_one in the serializer, the same embedded payload should be used to create, read and update the document (POST/GET/PUT).
Proposed Solution
See the ActiveModelSerializer test, for the various objects used to test the serializer. Adding a new SecretLab that belongsTo a SuperVillain (likewise the SuperVillain will belongTo the SecretLab, effecively a 1:1 relationship)
SecretLab = DS.Model.extend({
minionCapacity: DS.attr('number'),
vicinity: DS.attr('string'),
superVillain: DS.belongsTo('superVillain')
});
SuperVillain = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
homePlanet: DS.belongsTo("homePlanet"),
secretLab: DS.belongsTo("secretLab"),
evilMinions: DS.hasMany("evilMinion")
});
Given a choice made on the server to embed this 1:1 relationship,
the original payload from server (w/ embedded belongsTo object for secretLab) would look like:
{
"super_villain": {
"id": "1",
"first_name": "Tom",
"last_name": "Dale",
"home_planet_id": "123",
"evil_minion_ids": ["1", "2", "3"],
"secret_lab": {
"id": "101",
"minion_capacity": 5000,
"vicinity": "California, USA"
}
}
}
And for Ember data to parse the payload the format needs to be converted to the compound document format (side loaded). So using the extractSingle method the embedded belongsTo object would be extracted to the needed format like so:
{
"superVillain": {
"id": "1",
"firstName": "Tom",
"lastName": "Dale",
"homePlanet": "123",
"evilMinions": ["1", "2", "3"],
"secretLab": "101"
},
"secretLabs": [
{
"id": "101",
"minionCapacity": 5000,
"vicinity": "California, USA"
}
]
}
Likewise when serializing… setting a custom attrs configuration will be used to indicate that the model is always embedded:
App.SuperVillainSerializer = DS.ActiveModelSerializer.extend({
attrs: {
secretLab: {embedded: 'always'}
}
}));
Given a record created like so:
// record with id(s), persisted
var tom = this.store.createRecord(
'superVillain',
{ firstName: "Tom", lastName: "Dale", id: "1",
secretLab: this.store.createRecord('secretLab', { minionCapacity: 5000, vicinity: "California, USA", id: "101" }),
homePlanet: this.store.createRecord('homePlanet', { name: "Villain League", id: "123" })
}
);
A test to confirm that the SuperVillain is serialized with an embedded object would look like:
deepEqual(tom.serialize(), {
first_name: tom.get("firstName"),
last_name: tom.get("lastName"),
home_planet_id: tom.get("homePlanet").get("id"),
secret_lab: {
id: tom.get("secretLab").get("id"),
minion_capacity: tom.get("secretLab").get("minionCapacity"),
vicinity: tom.get("secretLab").get("vicinity")
}
});
And for a new record (no id for the villian or lab yet, planet is known):
var tom = this.store.createRecord(
'superVillain',
{ firstName: "Tom", lastName: "Dale",
secretLab: this.store.createRecord('secretLab', { minionCapacity: 5000, vicinity: "California, USA" }),
homePlanet: this.store.createRecord('homePlanet', { name: "Villain League", id: "123" })
}
);
A test to confirm the serialized record without any ids would be like:
deepEqual(tom.serialize(), {
first_name: tom.get("firstName"),
last_name: tom.get("lastName"),
home_planet_id: tom.get("homePlanet").get("id"),
secret_lab: {
minion_capacity: tom.get("secretLab").get("minionCapacity"),
vicinity: tom.get("secretLab").get("vicinity")
}
});
Discussion
I have already written a custom application serializer for a project I’m working on (which extends DS.ActiveModelSerializer). I’ve started to write failing tests for the above proposed solution in the ActiveModelSerializer test file. Personally I would like to extend the DS.ActiveModelSerializer to support embedded hasMany and belongsTo payloads using both embeds_many and embeds_one in model classes.
Is anyone interested in the proposed solution above?