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?