Skip to content

Commit

Permalink
Commit 1. EmbeddedRecordsMixin - Multiple Store Failing Test
Browse files Browse the repository at this point in the history
Commit 2. Fix Failing Test

Commit 3. Make Stores Manage Adapter & Serializer Instances

Commit 4. Refactor to utilize cache more effectively

- Added lookupAdapter and lookupSerializer - responsible for managing
local cached instances
- Added modelFor() to adapterFor() (consistent and discussed in PR
- Added to & clarified some documentation

Commit 5. Removed duplication

lookupSerializer and lookupAdapter were doing the same thing - made
them use a common function.
Undecided on whether to completely remove these two functions in favour
of using ‘retrieveManagedInstance’ in ‘adapterFor’ and ‘serializerFor’.
The case for leaving them there I think is purely readability/clarity.
  • Loading branch information
jmurphyau committed Jan 7, 2015
1 parent 9e9a781 commit 3e424a4
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 20 deletions.
3 changes: 3 additions & 0 deletions packages/ember-data/lib/initializers/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export default function initializeStore(container, application){
Ember.deprecate('Specifying a custom Store for Ember Data on your global namespace as `App.Store` ' +
'has been deprecated. Please use `App.ApplicationStore` instead.', !(application && application.Store));

container.optionsForType('serializer', { singleton: false });
container.optionsForType('adapter', { singleton: false });

container.register('store:main', container.lookupFactory('store:application') || (application && application.Store) || Store);

// allow older names to be looked up
Expand Down
1 change: 0 additions & 1 deletion packages/ember-data/lib/initializers/store_injections.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@
export default function initializeStoreInjections(container){
container.injection('controller', 'store', 'store:main');
container.injection('route', 'store', 'store:main');
container.injection('serializer', 'store', 'store:main');
container.injection('data-adapter', 'store', 'store:main');
}
90 changes: 75 additions & 15 deletions packages/ember-data/lib/system/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ Store = Ember.Object.extend({
store: this
});
this._pendingSave = [];
this._containerCache = Ember.create(null);
//Used to keep track of all the find requests that need to be coalesced
this._pendingFetch = Map.create();
},
Expand Down Expand Up @@ -250,7 +251,8 @@ Store = Ember.Object.extend({

if (DS.Adapter.detect(adapter)) {
adapter = adapter.create({
container: this.container
container: this.container,
store: this
});
}

Expand Down Expand Up @@ -1650,27 +1652,34 @@ Store = Ember.Object.extend({
// ......................

/**
Returns the adapter for a given type.
Returns an instance of the adapter for a given type. For
example, `adapterFor('person')` will return an instance of
`App.PersonAdapter`.
If no `App.PersonAdapter` is found, this method will look
for an `App.ApplicationAdapter` (the default adapter for
your entire application).
If no `App.ApplicationAdapter` is found, it will return
the value of the `defaultAdapter`.
@method adapterFor
@private
@param {subclass of DS.Model} type
@param {String or subclass of DS.Model} type
@return DS.Adapter
*/
adapterFor: function(type) {
var container = this.container, adapter;

if (container) {
adapter = container.lookup('adapter:' + type.typeKey) || container.lookup('adapter:application');
}
type = this.modelFor(type);

var adapter = this.lookupAdapter(type.typeKey) || this.lookupAdapter('application');

return adapter || get(this, 'defaultAdapter');
},

// ..............................
// . RECORD CHANGE NOTIFICATION .
// ..............................

/**
Returns an instance of the serializer for a given type. For
example, `serializerFor('person')` will return an instance of
Expand All @@ -1679,22 +1688,73 @@ Store = Ember.Object.extend({
If no `App.PersonSerializer` is found, this method will look
for an `App.ApplicationSerializer` (the default serializer for
your entire application).
If no `App.ApplicationSerializer` is found, it will fall back
if no `App.ApplicationSerializer` is found, it will attempt
to get the `defaultSerializer` from the `PersonAdapter`
(`adapterFor('person')`).
If a serializer cannot be found on the adapter, it will fall back
to an instance of `DS.JSONSerializer`.
@method serializerFor
@private
@param {String} type the record to serialize
@param {String or subclass of DS.Model} type the record to serialize
@return {DS.Serializer}
*/
serializerFor: function(type) {
type = this.modelFor(type);
var adapter = this.adapterFor(type);

return serializerFor(this.container, type.typeKey, adapter && adapter.defaultSerializer);
var serializer = this.lookupSerializer(type.typeKey) || this.lookupSerializer('application');

if (!serializer) {
var adapter = this.adapterFor(type);
serializer = this.lookupSerializer(adapter.defaultSerializer);
}

if (!serializer) {
serializer = this.lookupSerializer('-default');
}

return serializer;
},

/**
Retrieve a particular instance from the
container cache. If not found, creates it and
placing it in the cache.
Enabled a store to manage local instances of
adapters and serializers.
@method retrieveManagedInstance
@private
@param {String} type the object type
@param {String} type the object name
@return {Ember.Object}
*/
retrieveManagedInstance: function(type, name) {
var key = type+":"+name;

if (!this._containerCache[key]) {
var instance = this.container.lookup(key);

if (instance) {
set(instance, 'store', this);
this._containerCache[key] = instance;
}
}

return this._containerCache[key];
},

lookupAdapter: function(name) {
return this.retrieveManagedInstance('adapter', name);
},

lookupSerializer: function(name) {
return this.retrieveManagedInstance('serializer', name);
},

willDestroy: function() {
var typeMaps = this.typeMaps;
var keys = Ember.keys(typeMaps);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ module("integration/relationship/belongs_to Belongs-To Relationships", {
author: Author
});

env.container.optionsForType('serializer', { singleton: false });
env.container.optionsForType('adapter', { singleton: false });

env.container.register('serializer:user', DS.JSONSerializer.extend({
attrs: {
favouriteMessage: { embedded: 'always' }
Expand Down Expand Up @@ -169,7 +172,9 @@ test("The store can load a polymorphic belongsTo association", function() {
});

test("The store can serialize a polymorphic belongsTo association", function() {
env.serializer.serializePolymorphicType = function(record, json, relationship) {
var serializerInstance = store.serializerFor('comment');

serializerInstance.serializePolymorphicType = function(record, json, relationship) {
ok(true, "The serializer's serializePolymorphicType method should be called");
json["message_type"] = "post";
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ module("integration/embedded_records_mixin - EmbeddedRecordsMixin", {
env.store.modelFor('lightSaber');
env.store.modelFor('evilMinion');
env.store.modelFor('comment');
env.container.optionsForType('serializer', { singleton: false });
env.container.optionsForType('adapter', { singleton: false });
env.container.register('serializer:application', DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin));
env.container.register('serializer:-active-model', DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin));
env.container.register('adapter:-active-model', DS.ActiveModelAdapter);
Expand Down Expand Up @@ -1320,3 +1322,76 @@ test("serializing relationships with an embedded and without calls super when no
ok(calledSerializeBelongsTo);
ok(calledSerializeHasMany);
});

test("embedded records should be created in multiple stores", function() {

env.container.register('store:primary', DS.Store);
env.container.register('store:secondary', DS.Store);

env.primaryStore = env.container.lookup('store:primary');
env.secondaryStore = env.container.lookup('store:secondary');

env.container.register('adapter:superVillain', DS.ActiveModelAdapter);
env.container.register('adapter:homePlanet', DS.ActiveModelAdapter);

env.container.register('serializer:homePlanet', DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
villains: {embedded: 'always'}
}
}));

var serializer = env.store.serializerFor("homePlanet"),
serializer_primary = env.primaryStore.serializerFor("homePlanet"),
serializer_secondary = env.secondaryStore.serializerFor("homePlanet");

var json_hash = {
home_planet: {
id: "1",
name: "Earth",
villains: [{
id: "1",
first_name: "Tom",
last_name: "Dale"
}]
}
};
var json_hash_primary = {
home_planet: {
id: "1",
name: "Mars",
villains: [{
id: "1",
first_name: "James",
last_name: "Murphy"
}]
}
};
var json_hash_secondary = {
home_planet: {
id: "1",
name: "Saturn",
villains: [{
id: "1",
first_name: "Jade",
last_name: "John"
}]
}
};
var json, json_primary, json_secondary;

run(function(){
json = serializer.extractSingle(env.store, HomePlanet, json_hash);
equal(env.store.hasRecordForId("superVillain","1"), true, "superVillain should exist in store:main");
});

run(function(){
json_primary = serializer_primary.extractSingle(env.primaryStore, HomePlanet, json_hash_primary);
equal(env.primaryStore.hasRecordForId("superVillain","1"), true, "superVillain should exist in store:primary");
});

run(function(){
json_secondary = serializer_secondary.extractSingle(env.secondaryStore, HomePlanet, json_hash_secondary);
equal(env.secondaryStore.hasRecordForId("superVillain","1"), true, "superVillain should exist in store:secondary");
});

});
6 changes: 3 additions & 3 deletions packages/ember-data/tests/integration/setup-container-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ test("the deprecated serializer:_default is resolved as serializer:default", fun
deprecated = container.lookup('serializer:_default');
});

ok(deprecated === valid, "they should resolve to the same thing");
ok(deprecated.constructor === valid.constructor, "they should resolve to the same thing");
});

test("the deprecated serializer:_rest is resolved as serializer:rest", function(){
Expand All @@ -47,7 +47,7 @@ test("the deprecated serializer:_rest is resolved as serializer:rest", function(
deprecated = container.lookup('serializer:_rest');
});

ok(deprecated === valid, "they should resolve to the same thing");
ok(deprecated.constructor === valid.constructor, "they should resolve to the same thing");
});

test("the deprecated adapter:_rest is resolved as adapter:rest", function(){
Expand All @@ -56,7 +56,7 @@ test("the deprecated adapter:_rest is resolved as adapter:rest", function(){
deprecated = container.lookup('adapter:_rest');
});

ok(deprecated === valid, "they should resolve to the same thing");
ok(deprecated.constructor === valid.constructor, "they should resolve to the same thing");
});

test("a deprecation is made when looking up adapter:_rest", function(){
Expand Down
3 changes: 3 additions & 0 deletions tests/ember_configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@
adapter: adapter
}));

env.container.optionsForType('serializer', { singleton: false });
env.container.optionsForType('adapter', { singleton: false });

container.register('serializer:-default', DS.JSONSerializer);
container.register('serializer:-rest', DS.RESTSerializer);
container.register('adapter:-rest', DS.RESTAdapter);
Expand Down

0 comments on commit 3e424a4

Please sign in to comment.