From 6243347687da16b1700a02a28d7c3f48281e6580 Mon Sep 17 00:00:00 2001 From: Stanley Stuart Date: Sat, 16 May 2015 13:18:31 -0500 Subject: [PATCH 1/4] remove passing factories to store methods Previously, we allowed users to either pass factories (subclasses of DS.Model), or a string to store methods. For more consistency in the container-based world where things are looked up through strings, support for looking up via passing a factory has been removed. This is part of a refactor that will remove state (like the store) from Ember Data's factories in order to remove Ember.MODEL_FACTORY_INJECTIONS. --- .../lib/system/active-model-serializer.js | 6 +- ...el-serializer-namespaced-modelname-test.js | 29 +-- .../active-model-serializer-test.js | 48 ++-- .../lib/serializers/embedded-records-mixin.js | 10 +- .../lib/serializers/json-serializer.js | 2 +- .../lib/serializers/rest-serializer.js | 25 +- .../lib/system/debug/debug-adapter.js | 6 +- packages/ember-data/lib/system/many-array.js | 2 +- packages/ember-data/lib/system/model/model.js | 4 +- .../adapter-populated-record-array.js | 5 +- .../lib/system/record-arrays/record-array.js | 4 +- .../relationships/state/relationship.js | 6 +- packages/ember-data/lib/system/store.js | 231 ++++++++++-------- .../ember-data/lib/system/store/finders.js | 30 ++- .../adapter/build-url-mixin-test.js | 6 +- .../integration/adapter/find-all-test.js | 66 ++--- .../tests/integration/adapter/find-test.js | 77 +++--- .../integration/adapter/store-adapter-test.js | 2 +- .../tests/integration/debug-adapter-test.js | 9 +- .../tests/integration/filter-test.js | 8 +- .../tests/integration/inverse-test.js | 4 +- .../tests/integration/lifecycle-hooks-test.js | 4 +- .../tests/integration/multiple_stores_test.js | 26 +- .../tests/integration/records/load-test.js | 4 +- .../relationships/belongs-to-test.js | 2 +- .../polymorphic-mixins-belongs-to-test.js | 4 +- .../polymorphic-mixins-has-many-test.js | 4 +- .../embedded-records-mixin-test.js | 196 +++++++-------- .../serializers/json-serializer-test.js | 46 ++-- .../serializers/rest-serializer-test.js | 66 ++--- .../tests/integration/store-test.js | 2 +- .../adapter-populated-record-array-test.js | 12 +- packages/ember-data/tests/unit/debug-test.js | 4 +- packages/ember-data/tests/unit/model-test.js | 171 ++++++++----- .../unit/model/lifecycle-callbacks-test.js | 39 +-- .../ember-data/tests/unit/model/merge-test.js | 39 ++- .../model/relationships/belongs-to-test.js | 4 +- .../unit/model/relationships/has-many-test.js | 12 +- .../tests/unit/record-array-test.js | 76 ++++-- .../tests/unit/store/adapter-interop-test.js | 206 +++++++++------- .../tests/unit/store/create-record-test.js | 9 +- .../unit/store/has_record_for_id_test.js | 8 +- .../tests/unit/store/model-for-test.js | 10 - .../ember-data/tests/unit/store/push-test.js | 2 +- .../tests/unit/store/unload-test.js | 38 +-- 45 files changed, 868 insertions(+), 696 deletions(-) diff --git a/packages/activemodel-adapter/lib/system/active-model-serializer.js b/packages/activemodel-adapter/lib/system/active-model-serializer.js index adbacd106a4..366d28882db 100644 --- a/packages/activemodel-adapter/lib/system/active-model-serializer.js +++ b/packages/activemodel-adapter/lib/system/active-model-serializer.js @@ -117,12 +117,12 @@ var ActiveModelSerializer = RESTSerializer.extend({ relationship keys. @method keyForRelationship - @param {String} relationshipTypeKey + @param {String} relationshipModelName @param {String} kind @return String */ - keyForRelationship: function(relationshipTypeKey, kind) { - var key = decamelize(relationshipTypeKey); + keyForRelationship: function(relationshipModelName, kind) { + var key = decamelize(relationshipModelName); if (kind === "belongsTo") { return key + "_id"; } else if (kind === "hasMany") { diff --git a/packages/activemodel-adapter/tests/integration/active-model-serializer-namespaced-modelname-test.js b/packages/activemodel-adapter/tests/integration/active-model-serializer-namespaced-modelname-test.js index 27fa5b5cb5c..958281d5ac3 100644 --- a/packages/activemodel-adapter/tests/integration/active-model-serializer-namespaced-modelname-test.js +++ b/packages/activemodel-adapter/tests/integration/active-model-serializer-namespaced-modelname-test.js @@ -6,34 +6,35 @@ module("integration/active_model - AMS-namespaced-model-names", { SuperVillain = DS.Model.extend({ firstName: DS.attr('string'), lastName: DS.attr('string'), - evilMinions: DS.hasMany("evilMinion") + evilMinions: DS.hasMany('evil-minion') }); EvilMinion = DS.Model.extend({ - superVillain: DS.belongsTo('superVillain'), + superVillain: DS.belongsTo('super-villain'), name: DS.attr('string') }); YellowMinion = EvilMinion.extend(); DoomsdayDevice = DS.Model.extend({ name: DS.attr('string'), - evilMinion: DS.belongsTo('evilMinion', { polymorphic: true }) + evilMinion: DS.belongsTo('evil-minion', { polymorphic: true }) }); MediocreVillain = DS.Model.extend({ name: DS.attr('string'), - evilMinions: DS.hasMany('evilMinion', { polymorphic: true }) + evilMinions: DS.hasMany('evil-minion', { polymorphic: true }) }); env = setupStore({ superVillain: SuperVillain, evilMinion: EvilMinion, 'evilMinions/yellowMinion': YellowMinion, doomsdayDevice: DoomsdayDevice, - mediocreVillain: MediocreVillain + mediocreVillain: MediocreVillain, + yellowMinion: YellowMinion }); - env.store.modelFor('superVillain'); - env.store.modelFor('evilMinion'); - env.store.modelFor('evilMinions/yellowMinion'); - env.store.modelFor('doomsdayDevice'); - env.store.modelFor('mediocreVillain'); + env.store.modelFor('super-villain'); + env.store.modelFor('evil-minion'); + env.store.modelFor('evil-minions/yellow-minion'); + env.store.modelFor('doomsday-device'); + env.store.modelFor('mediocre-villain'); env.registry.register('serializer:application', DS.ActiveModelSerializer); env.registry.register('serializer:-active-model', DS.ActiveModelSerializer); env.registry.register('adapter:-active-model', DS.ActiveModelAdapter); @@ -49,8 +50,8 @@ module("integration/active_model - AMS-namespaced-model-names", { test("serialize polymorphic", function() { var tom, ray; run(function() { - tom = env.store.createRecord('evilMinions/yellowMinion', { name: "Alex", id: "124" }); - ray = env.store.createRecord(DoomsdayDevice, { evilMinion: tom, name: "DeathRay" }); + tom = env.store.createRecord('evil-minions/yellow-minion', { name: "Alex", id: "124" }); + ray = env.store.createRecord('doomsday-device', { evilMinion: tom, name: "DeathRay" }); }); var json = env.amsSerializer.serialize(ray._createSnapshot()); @@ -66,8 +67,8 @@ test("serialize polymorphic when type key is not camelized", function() { YellowMinion.modelName = 'evil-minions/yellow-minion'; var tom, ray; run(function() { - tom = env.store.createRecord(YellowMinion, { name: "Alex", id: "124" }); - ray = env.store.createRecord(DoomsdayDevice, { evilMinion: tom, name: "DeathRay" }); + tom = env.store.createRecord('yellow-minion', { name: "Alex", id: "124" }); + ray = env.store.createRecord('doomsday-device', { evilMinion: tom, name: "DeathRay" }); }); var json = env.amsSerializer.serialize(ray._createSnapshot()); diff --git a/packages/activemodel-adapter/tests/integration/active-model-serializer-test.js b/packages/activemodel-adapter/tests/integration/active-model-serializer-test.js index e0a56034a1b..a259615348a 100644 --- a/packages/activemodel-adapter/tests/integration/active-model-serializer-test.js +++ b/packages/activemodel-adapter/tests/integration/active-model-serializer-test.js @@ -7,25 +7,25 @@ module("integration/active_model - ActiveModelSerializer", { SuperVillain = DS.Model.extend({ firstName: DS.attr('string'), lastName: DS.attr('string'), - homePlanet: DS.belongsTo("homePlanet"), - evilMinions: DS.hasMany("evilMinion") + homePlanet: DS.belongsTo('home-planet'), + evilMinions: DS.hasMany('evil-minion') }); HomePlanet = DS.Model.extend({ name: DS.attr('string'), - superVillains: DS.hasMany('superVillain', { async: true }) + superVillains: DS.hasMany('super-villain', { async: true }) }); EvilMinion = DS.Model.extend({ - superVillain: DS.belongsTo('superVillain'), + superVillain: DS.belongsTo('super-villain'), name: DS.attr('string') }); YellowMinion = EvilMinion.extend(); DoomsdayDevice = DS.Model.extend({ name: DS.attr('string'), - evilMinion: DS.belongsTo('evilMinion', { polymorphic: true }) + evilMinion: DS.belongsTo('evil-minion', { polymorphic: true }) }); MediocreVillain = DS.Model.extend({ name: DS.attr('string'), - evilMinions: DS.hasMany('evilMinion', { polymorphic: true }) + evilMinions: DS.hasMany('evil-minion', { polymorphic: true }) }); env = setupStore({ superVillain: SuperVillain, @@ -35,12 +35,12 @@ module("integration/active_model - ActiveModelSerializer", { doomsdayDevice: DoomsdayDevice, mediocreVillain: MediocreVillain }); - env.store.modelFor('superVillain'); - env.store.modelFor('homePlanet'); - env.store.modelFor('evilMinion'); - env.store.modelFor('yellowMinion'); - env.store.modelFor('doomsdayDevice'); - env.store.modelFor('mediocreVillain'); + env.store.modelFor('super-villain'); + env.store.modelFor('home-planet'); + env.store.modelFor('evil-minion'); + env.store.modelFor('yellow-minion'); + env.store.modelFor('doomsday-device'); + env.store.modelFor('mediocre-villain'); env.registry.register('serializer:application', DS.ActiveModelSerializer); env.registry.register('serializer:-active-model', DS.ActiveModelSerializer); env.registry.register('adapter:-active-model', DS.ActiveModelAdapter); @@ -56,8 +56,8 @@ module("integration/active_model - ActiveModelSerializer", { test("serialize", function() { var tom; run(function() { - league = env.store.createRecord(HomePlanet, { name: "Villain League", id: "123" }); - tom = env.store.createRecord(SuperVillain, { firstName: "Tom", lastName: "Dale", homePlanet: league }); + league = env.store.createRecord('home-planet', { name: "Villain League", id: "123" }); + tom = env.store.createRecord('super-villain', { firstName: "Tom", lastName: "Dale", homePlanet: league }); }); var json = env.amsSerializer.serialize(tom._createSnapshot()); @@ -71,7 +71,7 @@ test("serialize", function() { test("serializeIntoHash", function() { run(function() { - league = env.store.createRecord(HomePlanet, { name: "Umber", id: "123" }); + league = env.store.createRecord('home-planet', { name: "Umber", id: "123" }); }); var json = {}; @@ -87,7 +87,7 @@ test("serializeIntoHash", function() { test("serializeIntoHash with decamelized types", function() { HomePlanet.modelName = 'home-planet'; run(function() { - league = env.store.createRecord(HomePlanet, { name: "Umber", id: "123" }); + league = env.store.createRecord('home-planet', { name: "Umber", id: "123" }); }); var json = {}; @@ -103,7 +103,7 @@ test("serializeIntoHash with decamelized types", function() { test("normalize", function() { SuperVillain.reopen({ - yellowMinion: DS.belongsTo('yellowMinion') + yellowMinion: DS.belongsTo('yellow-minion') }); var superVillain_hash = { first_name: "Tom", last_name: "Dale", home_planet_id: "123", evil_minion_ids: [1,2] }; @@ -156,7 +156,7 @@ test("extractSingle", function() { }); run(function() { - env.store.find("superVillain", 1).then(function(minion) { + env.store.find('super-villain', 1).then(function(minion) { equal(minion.get('firstName'), "Tom"); }); }); @@ -182,7 +182,7 @@ test("extractArray", function() { }]); run(function() { - env.store.find("superVillain", 1).then(function(minion) { + env.store.find('super-villain', 1).then(function(minion) { equal(minion.get('firstName'), "Tom"); }); }); @@ -191,8 +191,8 @@ test("extractArray", function() { test("serialize polymorphic", function() { var tom, ray; run(function() { - tom = env.store.createRecord(YellowMinion, { name: "Alex", id: "124" }); - ray = env.store.createRecord(DoomsdayDevice, { evilMinion: tom, name: "DeathRay" }); + tom = env.store.createRecord('yellow-minion', { name: "Alex", id: "124" }); + ray = env.store.createRecord('doomsday-device', { evilMinion: tom, name: "DeathRay" }); }); var json = env.amsSerializer.serialize(ray._createSnapshot()); @@ -208,8 +208,8 @@ test("serialize polymorphic when type key is not camelized", function() { YellowMinion.modelName = 'yellow-minion'; var tom, ray; run(function() { - tom = env.store.createRecord(YellowMinion, { name: "Alex", id: "124" }); - ray = env.store.createRecord(DoomsdayDevice, { evilMinion: tom, name: "DeathRay" }); + tom = env.store.createRecord('yellow-minion', { name: "Alex", id: "124" }); + ray = env.store.createRecord('doomsday-device', { evilMinion: tom, name: "DeathRay" }); }); var json = env.amsSerializer.serialize(ray._createSnapshot()); @@ -220,7 +220,7 @@ test("serialize polymorphic when type key is not camelized", function() { test("serialize polymorphic when associated object is null", function() { var ray, json; run(function() { - ray = env.store.createRecord(DoomsdayDevice, { name: "DeathRay" }); + ray = env.store.createRecord('doomsday-device', { name: "DeathRay" }); json = env.amsSerializer.serialize(ray._createSnapshot()); }); diff --git a/packages/ember-data/lib/serializers/embedded-records-mixin.js b/packages/ember-data/lib/serializers/embedded-records-mixin.js index d0c2be79e37..adc0ddcbc99 100644 --- a/packages/ember-data/lib/serializers/embedded-records-mixin.js +++ b/packages/ember-data/lib/serializers/embedded-records-mixin.js @@ -336,7 +336,7 @@ var EmbeddedRecordsMixin = Ember.Mixin.create({ var parentRecord = snapshot.type.inverseFor(relationship.key); if (parentRecord) { var name = parentRecord.name; - var embeddedSerializer = this.store.serializerFor(embeddedSnapshot.type); + var embeddedSerializer = this.store.serializerFor(embeddedSnapshot.modelName); var parentKey = embeddedSerializer.keyForRelationship(name, parentRecord.kind, 'deserialize'); if (parentKey) { delete json[parentKey]; @@ -423,7 +423,7 @@ function extractEmbeddedHasMany(store, key, embeddedTypeClass, hash) { var embeddedSerializer = store.serializerFor(embeddedTypeClass.modelName); forEach(hash[key], function(data) { var embeddedRecord = embeddedSerializer.normalize(embeddedTypeClass, data, null); - store.push(embeddedTypeClass, embeddedRecord); + store.push(embeddedTypeClass.modelName, embeddedRecord); ids.push(embeddedRecord.id); }); @@ -445,7 +445,7 @@ function extractEmbeddedHasManyPolymorphic(store, key, hash) { var primaryKey = get(embeddedSerializer, 'primaryKey'); var embeddedRecord = embeddedSerializer.normalize(embeddedTypeClass, data, null); - store.push(embeddedTypeClass, embeddedRecord); + store.push(embeddedTypeClass.modelName, embeddedRecord); ids.push({ id: embeddedRecord[primaryKey], type: modelName }); }); @@ -460,7 +460,7 @@ function extractEmbeddedBelongsTo(store, key, embeddedTypeClass, hash) { var embeddedSerializer = store.serializerFor(embeddedTypeClass.modelName); var embeddedRecord = embeddedSerializer.normalize(embeddedTypeClass, hash[key], null); - store.push(embeddedTypeClass, embeddedRecord); + store.push(embeddedTypeClass.modelName, embeddedRecord); hash[key] = embeddedRecord.id; //TODO Need to add a reference to the parent later so relationship works between both `belongsTo` records @@ -479,7 +479,7 @@ function extractEmbeddedBelongsToPolymorphic(store, key, hash) { var primaryKey = get(embeddedSerializer, 'primaryKey'); var embeddedRecord = embeddedSerializer.normalize(embeddedTypeClass, data, null); - store.push(embeddedTypeClass, embeddedRecord); + store.push(embeddedTypeClass.modelName, embeddedRecord); hash[key] = embeddedRecord[primaryKey]; hash[key + 'Type'] = modelName; diff --git a/packages/ember-data/lib/serializers/json-serializer.js b/packages/ember-data/lib/serializers/json-serializer.js index 7a03adaa7d4..e81133c090b 100644 --- a/packages/ember-data/lib/serializers/json-serializer.js +++ b/packages/ember-data/lib/serializers/json-serializer.js @@ -729,7 +729,7 @@ export default Serializer.extend({ @return {Object} json The deserialized payload */ extract: function(store, typeClass, payload, id, requestType) { - this.extractMeta(store, typeClass, payload); + this.extractMeta(store, typeClass.modelName, payload); var specificExtract = "extract" + requestType.charAt(0).toUpperCase() + requestType.substr(1); return this[specificExtract](store, typeClass, payload, id, requestType); diff --git a/packages/ember-data/lib/serializers/rest-serializer.js b/packages/ember-data/lib/serializers/rest-serializer.js index 3431246c4b4..2a96622ac68 100644 --- a/packages/ember-data/lib/serializers/rest-serializer.js +++ b/packages/ember-data/lib/serializers/rest-serializer.js @@ -267,14 +267,14 @@ var RESTSerializer = JSONSerializer.extend({ var primaryRecord; for (var prop in payload) { - var typeName = this.modelNameFromPayloadKey(prop); + var modelName = this.modelNameFromPayloadKey(prop); - if (!store.modelFactoryFor(typeName)) { - Ember.warn(this.warnMessageNoModelForKey(prop, typeName), false); + if (!store.modelFactoryFor(modelName)) { + Ember.warn(this.warnMessageNoModelForKey(prop, modelName), false); continue; } - var type = store.modelFor(typeName); - var isPrimary = type.modelName === primaryTypeClassName; + var typeClass = store.modelFor(modelName); + var isPrimary = typeClass.modelName === primaryTypeClassName; var value = payload[prop]; if (value === null) { @@ -290,10 +290,9 @@ var RESTSerializer = JSONSerializer.extend({ /*jshint loopfunc:true*/ forEach.call(value, function(hash) { var typeName = this.modelNameFromPayloadKey(prop); - var type = store.modelFor(typeName); - var typeSerializer = store.serializerFor(type); + var typeSerializer = store.serializerFor(typeName); - hash = typeSerializer.normalize(type, hash, prop); + hash = typeSerializer.normalize(typeClass, hash, prop); var isFirstCreatedRecord = isPrimary && !recordId && !primaryRecord; var isUpdatedRecord = isPrimary && coerceId(hash.id) === recordId; @@ -307,7 +306,7 @@ var RESTSerializer = JSONSerializer.extend({ if (isFirstCreatedRecord || isUpdatedRecord) { primaryRecord = hash; } else { - store.push(typeName, hash); + store.push(modelName, hash); } }, this); } @@ -435,7 +434,7 @@ var RESTSerializer = JSONSerializer.extend({ continue; } var type = store.modelFor(typeName); - var typeSerializer = store.serializerFor(type); + var typeSerializer = store.serializerFor(typeName); var isPrimary = (!forcedSecondary && (type.modelName === primaryTypeClassName)); /*jshint loopfunc:true*/ @@ -493,12 +492,12 @@ var RESTSerializer = JSONSerializer.extend({ Ember.warn(this.warnMessageNoModelForKey(prop, modelName), false); continue; } - var type = store.modelFor(modelName); - var typeSerializer = store.serializerFor(type); + var typeClass = store.modelFor(modelName); + var typeSerializer = store.serializerFor(modelName); /*jshint loopfunc:true*/ var normalizedArray = map.call(Ember.makeArray(payload[prop]), function(hash) { - return typeSerializer.normalize(type, hash, prop); + return typeSerializer.normalize(typeClass, hash, prop); }, this); store.pushMany(modelName, normalizedArray); diff --git a/packages/ember-data/lib/system/debug/debug-adapter.js b/packages/ember-data/lib/system/debug/debug-adapter.js index 3b7681466c7..fb81957483a 100644 --- a/packages/ember-data/lib/system/debug/debug-adapter.js +++ b/packages/ember-data/lib/system/debug/debug-adapter.js @@ -42,7 +42,11 @@ export default Ember.DataAdapter.extend({ return columns; }, - getRecords: function(modelName) { + getRecords: function(modelNameOrFactory) { + // TODO: Ask Teddy what we should do here. + // Ideally this should always get passed a string. + + var modelName = typeof modelNameOrFactory === 'string' ? modelNameOrFactory : modelNameOrFactory.modelName; return this.get('store').all(modelName); }, diff --git a/packages/ember-data/lib/system/many-array.js b/packages/ember-data/lib/system/many-array.js index be0ff78d696..e4b3bbf15cc 100644 --- a/packages/ember-data/lib/system/many-array.js +++ b/packages/ember-data/lib/system/many-array.js @@ -228,7 +228,7 @@ export default Ember.Object.extend(Ember.MutableArray, Ember.Evented, { Ember.assert("You cannot add '" + type.modelName + "' records to this polymorphic relationship.", !get(this, 'isPolymorphic')); - record = store.createRecord(type, hash); + record = store.createRecord(type.modelName, hash); this.pushObject(record); return record; diff --git a/packages/ember-data/lib/system/model/model.js b/packages/ember-data/lib/system/model/model.js index 85f9caf5f7e..fbdbe98011f 100644 --- a/packages/ember-data/lib/system/model/model.js +++ b/packages/ember-data/lib/system/model/model.js @@ -29,13 +29,13 @@ var _splitOnDotCache = Ember.create(null); function splitOnDot(name) { return _splitOnDotCache[name] || ( - _splitOnDotCache[name] = name.split('.') + (_splitOnDotCache[name] = name.split('.')) ); } function extractPivotName(name) { return _extractPivotNameCache[name] || ( - _extractPivotNameCache[name] = splitOnDot(name)[0] + (_extractPivotNameCache[name] = splitOnDot(name)[0]) ); } diff --git a/packages/ember-data/lib/system/record-arrays/adapter-populated-record-array.js b/packages/ember-data/lib/system/record-arrays/adapter-populated-record-array.js index 5712a5a142a..1d7a24f00e7 100644 --- a/packages/ember-data/lib/system/record-arrays/adapter-populated-record-array.js +++ b/packages/ember-data/lib/system/record-arrays/adapter-populated-record-array.js @@ -39,8 +39,9 @@ export default RecordArray.extend({ load: function(data) { var store = get(this, 'store'); var type = get(this, 'type'); - var records = store.pushMany(type, data); - var meta = store.metadataFor(type); + var modelName = type.modelName; + var records = store.pushMany(modelName, data); + var meta = store.metadataFor(modelName); this.setProperties({ content: Ember.A(records), diff --git a/packages/ember-data/lib/system/record-arrays/record-array.js b/packages/ember-data/lib/system/record-arrays/record-array.js index da3a0409618..a1c98541f6d 100644 --- a/packages/ember-data/lib/system/record-arrays/record-array.js +++ b/packages/ember-data/lib/system/record-arrays/record-array.js @@ -113,9 +113,9 @@ export default Ember.ArrayProxy.extend(Ember.Evented, { if (get(this, 'isUpdating')) { return; } var store = get(this, 'store'); - var type = get(this, 'type'); + var modelName = get(this, 'type.modelName'); - return store.fetchAll(type, this); + return store.fetchAll(modelName, this); }, /** diff --git a/packages/ember-data/lib/system/relationships/state/relationship.js b/packages/ember-data/lib/system/relationships/state/relationship.js index f17715dc7fb..4dffdee23f1 100644 --- a/packages/ember-data/lib/system/relationships/state/relationship.js +++ b/packages/ember-data/lib/system/relationships/state/relationship.js @@ -2,7 +2,7 @@ import OrderedSet from "ember-data/system/ordered-set"; var forEach = Ember.EnumerableUtils.forEach; -var Relationship = function(store, record, inverseKey, relationshipMeta) { +function Relationship(store, record, inverseKey, relationshipMeta) { this.members = new OrderedSet(); this.canonicalMembers = new OrderedSet(); this.store = store; @@ -13,10 +13,10 @@ var Relationship = function(store, record, inverseKey, relationshipMeta) { this.relationshipMeta = relationshipMeta; //This probably breaks for polymorphic relationship in complex scenarios, due to //multiple possible modelNames - this.inverseKeyForImplicit = this.store.modelFor(this.record.constructor).modelName + this.key; + this.inverseKeyForImplicit = this.record.constructor.modelName + this.key; this.linkPromise = null; this.hasData = false; -}; +} Relationship.prototype = { constructor: Relationship, diff --git a/packages/ember-data/lib/system/store.js b/packages/ember-data/lib/system/store.js index 3a21b1d43f3..bc84050d148 100644 --- a/packages/ember-data/lib/system/store.js +++ b/packages/ember-data/lib/system/store.js @@ -41,9 +41,7 @@ import { import RecordArrayManager from "ember-data/system/record-array-manager"; import Model from "ember-data/system/model"; - -//Stanley told me to do this -var Backburner = Ember.__loader.require('backburner')['default'] || Ember.__loader.require('backburner')['Backburner']; +var Backburner = Ember.Backburner || Ember.__loader.require('backburner')['default'] || Ember.__loader.require('backburner')['Backburner']; //Shim Backburner.join if (!Backburner.prototype.join) { @@ -306,6 +304,7 @@ Store = Service.extend({ @return {DS.Model} record */ createRecord: function(modelName, inputProperties) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); var typeClass = this.modelFor(modelName); var properties = copy(inputProperties) || {}; @@ -315,7 +314,7 @@ Store = Service.extend({ // to avoid conflicts. if (isNone(properties.id)) { - properties.id = this._generateId(typeClass, properties); + properties.id = this._generateId(modelName, properties); } // Coerce ID to a string @@ -512,6 +511,7 @@ Store = Service.extend({ find: function(modelName, id, preload) { Ember.assert("You need to pass a type to the store's find method", arguments.length >= 1); Ember.assert("You may not pass `" + id + "` as id to the store's find method", arguments.length === 1 || !Ember.isNone(id)); + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); if (arguments.length === 1) { return this.findAll(modelName); @@ -552,6 +552,7 @@ Store = Service.extend({ @return {Promise} promise */ fetchById: function(modelName, id, preload) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); if (this.hasRecordForId(modelName, id)) { return this.getById(modelName, id).reload(); } else { @@ -568,6 +569,7 @@ Store = Service.extend({ @return {Promise} promise */ fetchAll: function(modelName) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); var typeClass = this.modelFor(modelName); return this._fetchAll(typeClass, this.all(modelName)); @@ -582,6 +584,7 @@ Store = Service.extend({ @deprecated Use [fetchById](#method_fetchById) instead */ fetch: function(modelName, id, preload) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); Ember.deprecate('Using store.fetch() has been deprecated. Use store.fetchById for fetching individual records or store.fetchAll for collections'); return this.fetchById(modelName, id, preload); }, @@ -597,9 +600,9 @@ Store = Service.extend({ @return {Promise} promise */ findById: function(modelName, id, preload) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); - var typeClass = this.modelFor(modelName); - var record = this.recordForId(typeClass, id); + var record = this.recordForId(modelName, id); return this._findByRecord(record, preload); }, @@ -632,6 +635,7 @@ Store = Service.extend({ @return {Promise} promise */ findByIds: function(modelName, ids) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); var store = this; return promiseArray(Ember.RSVP.all(map(ids, function(id) { @@ -652,7 +656,7 @@ Store = Service.extend({ fetchRecord: function(record) { var typeClass = record.constructor; var id = get(record, 'id'); - var adapter = this.adapterFor(typeClass); + var adapter = this.adapterFor(typeClass.modelName); Ember.assert("You tried to find a record but you have no adapter (for " + typeClass + ")", adapter); Ember.assert("You tried to find a record but your adapter (for " + typeClass + ") does not implement 'find'", typeof adapter.find === 'function'); @@ -700,7 +704,7 @@ Store = Service.extend({ _flushPendingFetchForType: function (recordResolverPairs, typeClass) { var store = this; - var adapter = store.adapterFor(typeClass); + var adapter = store.adapterFor(typeClass.modelName); var shouldCoalesce = !!adapter.findMany && adapter.coalesceFindRequests; var records = Ember.A(recordResolverPairs).mapBy('record'); @@ -802,13 +806,14 @@ Store = Service.extend({ ``` @method getById - @param {String or subclass of DS.Model} type + @param {String} modelName @param {String|Integer} id @return {DS.Model|null} record */ - getById: function(type, id) { - if (this.hasRecordForId(type, id)) { - return this.recordForId(type, id); + getById: function(modelName, id) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); + if (this.hasRecordForId(modelName, id)) { + return this.recordForId(modelName, id); } else { return null; } @@ -828,7 +833,7 @@ Store = Service.extend({ */ reloadRecord: function(record) { var type = record.constructor; - var adapter = this.adapterFor(type); + var adapter = this.adapterFor(type.modelName); var id = get(record, 'id'); Ember.assert("You cannot reload a record without an ID", id); @@ -847,6 +852,7 @@ Store = Service.extend({ @return {Boolean} */ hasRecordForId: function(modelName, inputId) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); var typeClass = this.modelFor(modelName); var id = coerceId(inputId); var record = this.typeMapFor(typeClass).idToRecord[id]; @@ -864,6 +870,7 @@ Store = Service.extend({ @return {DS.Model} record */ recordForId: function(modelName, inputId) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); var typeClass = this.modelFor(modelName); var id = coerceId(inputId); var idToRecord = this.typeMapFor(typeClass).idToRecord; @@ -912,7 +919,7 @@ Store = Service.extend({ @return {Promise} promise */ findHasMany: function(owner, link, type) { - var adapter = this.adapterFor(owner.constructor); + var adapter = this.adapterFor(owner.constructor.modelName); Ember.assert("You tried to load a hasMany relationship but you have no adapter (for " + owner.constructor + ")", adapter); Ember.assert("You tried to load a hasMany relationship from a specified `link` in the original payload but your adapter does not implement `findHasMany`", typeof adapter.findHasMany === 'function'); @@ -929,7 +936,7 @@ Store = Service.extend({ @return {Promise} promise */ findBelongsTo: function(owner, link, relationship) { - var adapter = this.adapterFor(owner.constructor); + var adapter = this.adapterFor(owner.constructor.modelName); Ember.assert("You tried to load a belongsTo relationship but you have no adapter (for " + owner.constructor + ")", adapter); Ember.assert("You tried to load a belongsTo relationship from a specified `link` in the original payload but your adapter does not implement `findBelongsTo`", typeof adapter.findBelongsTo === 'function'); @@ -950,21 +957,22 @@ Store = Service.extend({ @method findQuery @private - @param {String or subclass of DS.Model} type + @param {String} modelName @param {any} query an opaque query to be used by the adapter @return {Promise} promise */ - findQuery: function(typeName, query) { - var type = this.modelFor(typeName); + findQuery: function(modelName, query) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); + var typeClass = this.modelFor(modelName); var array = this.recordArrayManager - .createAdapterPopulatedRecordArray(type, query); + .createAdapterPopulatedRecordArray(typeClass, query); - var adapter = this.adapterFor(type); + var adapter = this.adapterFor(modelName); - Ember.assert("You tried to load a query but you have no adapter (for " + type + ")", adapter); + Ember.assert("You tried to load a query but you have no adapter (for " + typeClass + ")", adapter); Ember.assert("You tried to load a query but your adapter does not implement `findQuery`", typeof adapter.findQuery === 'function'); - return promiseArray(_findQuery(adapter, this, type, query, array)); + return promiseArray(_findQuery(adapter, this, typeClass, query, array)); }, /** @@ -978,6 +986,7 @@ Store = Service.extend({ @return {DS.AdapterPopulatedRecordArray} */ findAll: function(modelName) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); return this.fetchAll(modelName); }, @@ -989,7 +998,7 @@ Store = Service.extend({ @return {Promise} promise */ _fetchAll: function(typeClass, array) { - var adapter = this.adapterFor(typeClass); + var adapter = this.adapterFor(typeClass.modelName); var sinceToken = this.typeMapFor(typeClass).metadata.since; set(array, 'isUpdating', true); @@ -1003,6 +1012,7 @@ Store = Service.extend({ /** @method didUpdateAll @param {DS.Model} typeClass + @private */ didUpdateAll: function(typeClass) { var findAllCache = this.typeMapFor(typeClass).findAllCache; @@ -1033,6 +1043,7 @@ Store = Service.extend({ @return {DS.RecordArray} */ all: function(modelName) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); var typeClass = this.modelFor(modelName); var typeMap = this.typeMapFor(typeClass); var findAllCache = typeMap.findAllCache; @@ -1062,6 +1073,7 @@ Store = Service.extend({ @param {String} optional modelName */ unloadAll: function(modelName) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), !modelName || typeof modelName === 'string'); if (arguments.length === 0) { var typeMaps = this.typeMaps; var keys = Ember.keys(typeMaps); @@ -1086,7 +1098,7 @@ Store = Service.extend({ } function byType(entry) { - return typeMaps[entry]['type']; + return typeMaps[entry]['type'].modelName; } }, @@ -1137,12 +1149,13 @@ Store = Service.extend({ ``` @method filter - @param {String or subclass of DS.Model} type + @param {String} modelName @param {Object} query optional query @param {Function} filter @return {DS.PromiseArray} */ - filter: function(type, query, filter) { + filter: function(modelName, query, filter) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); var promise; var length = arguments.length; var array; @@ -1150,24 +1163,24 @@ Store = Service.extend({ // allow an optional server query if (hasQuery) { - promise = this.findQuery(type, query); + promise = this.findQuery(modelName, query); } else if (arguments.length === 2) { filter = query; } - type = this.modelFor(type); + modelName = this.modelFor(modelName); if (hasQuery) { - array = this.recordArrayManager.createFilteredRecordArray(type, filter, query); + array = this.recordArrayManager.createFilteredRecordArray(modelName, filter, query); } else { - array = this.recordArrayManager.createFilteredRecordArray(type, filter); + array = this.recordArrayManager.createFilteredRecordArray(modelName, filter); } promise = promise || Promise.cast(array); return promiseArray(promise.then(function() { return array; - }, null, "DS: Store#filter of " + type)); + }, null, "DS: Store#filter of " + modelName)); }, /** @@ -1185,24 +1198,26 @@ Store = Service.extend({ ``` @method recordIsLoaded - @param {String or subclass of DS.Model} type + @param {String} modelName @param {string} id @return {boolean} */ - recordIsLoaded: function(type, id) { - if (!this.hasRecordForId(type, id)) { return false; } - return !get(this.recordForId(type, id), 'isEmpty'); + recordIsLoaded: function(modelName, id) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); + if (!this.hasRecordForId(modelName, id)) { return false; } + return !get(this.recordForId(modelName, id), 'isEmpty'); }, /** This method returns the metadata for a specific type. @method metadataFor - @param {String or subclass of DS.Model} typeName + @param {String or subclass of DS.Model} modelName @return {object} */ - metadataFor: function(typeName) { - var typeClass = this.modelFor(typeName); + metadataFor: function(modelName) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); + var typeClass = this.modelFor(modelName); return this.typeMapFor(typeClass).metadata; }, @@ -1210,12 +1225,13 @@ Store = Service.extend({ This method sets the metadata for a specific type. @method setMetadataFor - @param {String or subclass of DS.Model} typeName + @param {String} modelName @param {Object} metadata metadata to set @return {object} */ - setMetadataFor: function(typeName, metadata) { - var typeClass = this.modelFor(typeName); + setMetadataFor: function(modelName, metadata) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); + var typeClass = this.modelFor(modelName); Ember.merge(this.typeMapFor(typeClass).metadata, metadata); }, @@ -1273,7 +1289,7 @@ Store = Service.extend({ forEach(pending, function(tuple) { var record = tuple[0]; var resolver = tuple[1]; - var adapter = this.adapterFor(record.constructor); + var adapter = this.adapterFor(record.constructor.modelName); var operation; if (get(record, 'currentState.stateName') === 'root.deleted.saved') { @@ -1429,14 +1445,14 @@ Store = Service.extend({ */ _modelForMixin: function(modelName) { - var normalizedTypeKey = normalizeModelName(modelName); + var normalizedModelName = normalizeModelName(modelName); var registry = this.container._registry ? this.container._registry : this.container; - var mixin = registry.resolve('mixin:' + normalizedTypeKey); + var mixin = registry.resolve('mixin:' + normalizedModelName); if (mixin) { //Cache the class as a model - registry.register('model:' + normalizedTypeKey, DS.Model.extend(mixin)); + registry.register('model:' + normalizedModelName, DS.Model.extend(mixin)); } - var factory = this.modelFactoryFor(normalizedTypeKey); + var factory = this.modelFactoryFor(normalizedModelName); if (factory) { factory.__isMixin = true; factory.__mixin = mixin; @@ -1451,29 +1467,21 @@ Store = Service.extend({ etc.) @method modelFor - @param {String or subclass of DS.Model} key + @param {String} modelName @return {subclass of DS.Model} */ - modelFor: function(key) { - var factory; - - if (typeof key === 'string') { - factory = this.modelFactoryFor(key); - if (!factory) { - //Support looking up mixins as base types for polymorphic relationships - factory = this._modelForMixin(key); - } - if (!factory) { - throw new Ember.Error("No model was found for '" + key + "'"); - } - factory.modelName = factory.modelName || normalizeModelName(key); - } else { - // A factory already supplied. Ensure it has a normalized key. - factory = key; - if (factory.modelName) { - factory.modelName = normalizeModelName(factory.modelName); - } + modelFor: function(modelName) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); + + var factory = this.modelFactoryFor(modelName); + if (!factory) { + //Support looking up mixins as base types for polymorphic relationships + factory = this._modelForMixin(modelName); } + if (!factory) { + throw new Ember.Error("No model was found for '" + modelName + "'"); + } + factory.modelName = factory.modelName || normalizeModelName(modelName); // deprecate typeKey if (!('typeKey' in factory)) { @@ -1494,8 +1502,9 @@ Store = Service.extend({ return factory; }, - modelFactoryFor: function(key) { - var normalizedKey = normalizeModelName(key); + modelFactoryFor: function(modelName) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); + var normalizedKey = normalizeModelName(modelName); return this.container.lookupFactory('model:' + normalizedKey); }, @@ -1561,12 +1570,13 @@ Store = Service.extend({ records, as well as to update existing records. @method push - @param {String or subclass of DS.Model} modelName + @param {String} modelName @param {Object} data @return {DS.Model} the record that was created or updated. */ push: function(modelName, data) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); Ember.assert("Expected an object as `data` in a call to `push` for " + modelName + " , but was " + data, Ember.typeOf(data) === 'object'); Ember.assert("You must include an `id` for " + modelName + " in an object passed to `push`", data.id != null && data.id !== ''); @@ -1588,9 +1598,9 @@ Store = Service.extend({ // Actually load the record into the store. - this._load(type, data); + this._load(modelName, data); - var record = this.recordForId(type, data.id); + var record = this.recordForId(modelName, data.id); var store = this; this._backburner.join(function() { @@ -1656,19 +1666,20 @@ Store = Service.extend({ ``` @method pushPayload - @param {String} type Optionally, a model used to determine which serializer will be used + @param {String} modelName Optionally, a model type used to determine which serializer will be used @param {Object} payload */ - pushPayload: function (type, inputPayload) { + pushPayload: function (modelName, inputPayload) { var serializer; var payload; if (!inputPayload) { - payload = type; + payload = modelName; serializer = defaultSerializer(this.container); - Ember.assert("You cannot use `store#pushPayload` without a type unless your default serializer defines `pushPayload`", typeof serializer.pushPayload === 'function'); + Ember.assert("You cannot use `store#pushPayload` without a modelName unless your default serializer defines `pushPayload`", typeof serializer.pushPayload === 'function'); } else { payload = inputPayload; - serializer = this.serializerFor(type); + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); + serializer = this.serializerFor(modelName); } var store = this; this._adapterRun(function() { @@ -1691,26 +1702,28 @@ Store = Service.extend({ ``` @method normalize - @param {String} type The name of the model type for this payload + @param {String} modelName The name of the model type for this payload @param {Object} payload @return {Object} The normalized payload */ - normalize: function (type, payload) { - var serializer = this.serializerFor(type); - var model = this.modelFor(type); + normalize: function (modelName, payload) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); + var serializer = this.serializerFor(modelName); + var model = this.modelFor(modelName); return serializer.normalize(model, payload); }, /** @method update - @param {String} type + @param {String} modelName @param {Object} data @return {DS.Model} the record that was updated. @deprecated Use [push](#method_push) instead */ - update: function(type, data) { + update: function(modelName, data) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); Ember.deprecate('Using store.update() has been deprecated since store.push() now handles partial updates. You should use store.push() instead.'); - return this.push(type, data); + return this.push(modelName, data); }, /** @@ -1719,16 +1732,17 @@ Store = Service.extend({ call `push` repeatedly for you. @method pushMany - @param {String or subclass of DS.Model} type + @param {String} modelName @param {Array} datas @return {Array} */ - pushMany: function(type, datas) { + pushMany: function(modelName, datas) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); var length = datas.length; var result = new Array(length); for (var i = 0; i < length; i++) { - result[i] = this.push(type, datas[i]); + result[i] = this.push(modelName, datas[i]); } return result; @@ -1736,13 +1750,14 @@ Store = Service.extend({ /** @method metaForType - @param {String or subclass of DS.Model} typeName + @param {String or subclass of DS.Model} modelName @param {Object} metadata @deprecated Use [setMetadataFor](#method_setMetadataFor) instead */ - metaForType: function(typeName, metadata) { + metaForType: function(modelName, metadata) { + Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); Ember.deprecate('Using store.metaForType() has been deprecated. Use store.setMetadataFor() to set metadata for a specific type.'); - this.setMetadataFor(typeName, metadata); + this.setMetadataFor(modelName, metadata); }, /** @@ -1847,15 +1862,21 @@ Store = Service.extend({ @method adapterFor @private - @param {String or subclass of DS.Model} type + @param {String} modelName @return DS.Adapter */ - adapterFor: function(type) { - if (type !== 'application') { - type = this.modelFor(type); + adapterFor: function(modelOrClass) { + var modelName; + + Ember.deprecate('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelOrClass === 'string'); + + if (typeof modelOrClass !== 'string') { + modelName = modelOrClass.modelName; + } else { + modelName = modelOrClass; } - var adapter = this.lookupAdapter(type.modelName) || this.lookupAdapter('application'); + var adapter = this.lookupAdapter(modelName) || this.lookupAdapter('application'); return adapter || get(this, 'defaultAdapter'); }, @@ -1886,18 +1907,23 @@ Store = Service.extend({ @method serializerFor @private - @param {String or subclass of DS.Model} type the record to serialize + @param {String} modelName the record to serialize @return {DS.Serializer} */ - serializerFor: function(type) { - if (type !== 'application') { - type = this.modelFor(type); + serializerFor: function(modelOrClass) { + var modelName; + + Ember.deprecate('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelOrClass === 'string'); + if (typeof modelOrClass !== 'string') { + modelName = modelOrClass.modelName; + } else { + modelName = modelOrClass; } - var serializer = this.lookupSerializer(type.modelName) || this.lookupSerializer('application'); + var serializer = this.lookupSerializer(modelName) || this.lookupSerializer('application'); if (!serializer) { - var adapter = this.adapterFor(type); + var adapter = this.adapterFor(modelName); serializer = this.lookupSerializer(get(adapter, 'defaultSerializer')); } @@ -1986,7 +2012,7 @@ function deserializeRecordId(store, data, key, relationship, id) { if (typeof id === 'number' || typeof id === 'string') { type = typeFor(relationship, key, data); - data[key] = store.recordForId(type, id); + data[key] = store.recordForId(typeof type === 'string' ? type : type.modelName, id); } else if (typeof id === 'object') { // hasMany polymorphic Ember.assert('Ember Data expected a number or string to represent the record(s) in the `' + relationship.key + '` relationship instead it found an object. If this is a polymorphic relationship please specify a `type` key. If this is an embedded relationship please include the `DS.EmbeddedRecordsMixin` and specify the `' + relationship.key +'` property in your serializer\'s attrs object.', id.type); @@ -2024,9 +2050,10 @@ function defaultSerializer(container) { function _commit(adapter, store, operation, record) { var type = record.constructor; + var modelName = type.modelName; var snapshot = record._createSnapshot(); var promise = adapter[operation](store, type, snapshot); - var serializer = serializerForAdapter(store, adapter, type); + var serializer = serializerForAdapter(store, adapter, modelName); var label = "DS: Extract and notify about " + operation + " completion of " + record; Ember.assert("Your adapter's '" + operation + "' method must return a value, but it returned `undefined", promise !==undefined); diff --git a/packages/ember-data/lib/system/store/finders.js b/packages/ember-data/lib/system/store/finders.js index 4697be92462..c633f5f3252 100644 --- a/packages/ember-data/lib/system/store/finders.js +++ b/packages/ember-data/lib/system/store/finders.js @@ -13,9 +13,10 @@ var get = Ember.get; var Promise = Ember.RSVP.Promise; export function _find(adapter, store, typeClass, id, record) { + var modelName = typeClass.modelName; var snapshot = record._createSnapshot(); var promise = adapter.find(store, typeClass, id, snapshot); - var serializer = serializerForAdapter(store, adapter, typeClass); + var serializer = serializerForAdapter(store, adapter, modelName); var label = "DS: Handle Adapter#find of " + typeClass + " with id: " + id; promise = Promise.cast(promise, label); @@ -26,7 +27,7 @@ export function _find(adapter, store, typeClass, id, record) { return store._adapterRun(function() { var payload = serializer.extract(store, typeClass, adapterPayload, id, 'find'); - return store.push(typeClass, payload); + return store.push(modelName, payload); }); }, function(error) { record.notFound(); @@ -40,9 +41,10 @@ export function _find(adapter, store, typeClass, id, record) { export function _findMany(adapter, store, typeClass, ids, records) { + var modelName = typeClass.modelName; var snapshots = Ember.A(records).invoke('_createSnapshot'); var promise = adapter.findMany(store, typeClass, ids, snapshots); - var serializer = serializerForAdapter(store, adapter, typeClass); + var serializer = serializerForAdapter(store, adapter, modelName); var label = "DS: Handle Adapter#findMany of " + typeClass; if (promise === undefined) { @@ -58,15 +60,16 @@ export function _findMany(adapter, store, typeClass, ids, records) { Ember.assert("The response from a findMany must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array'); - return store.pushMany(typeClass, payload); + return store.pushMany(modelName, payload); }); }, null, "DS: Extract payload of " + typeClass); } export function _findHasMany(adapter, store, record, link, relationship) { var snapshot = record._createSnapshot(); + var modelName = relationship.type.modelName; var promise = adapter.findHasMany(store, snapshot, link, relationship); - var serializer = serializerForAdapter(store, adapter, relationship.type); + var serializer = serializerForAdapter(store, adapter, modelName); var label = "DS: Handle Adapter#findHasMany of " + record + " : " + relationship.type; promise = Promise.cast(promise, label); @@ -79,16 +82,17 @@ export function _findHasMany(adapter, store, record, link, relationship) { Ember.assert("The response from a findHasMany must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array'); - var records = store.pushMany(relationship.type, payload); + var records = store.pushMany(modelName, payload); return records; }); }, null, "DS: Extract payload of " + record + " : hasMany " + relationship.type); } export function _findBelongsTo(adapter, store, record, link, relationship) { + var modelName = relationship.type.modelName; var snapshot = record._createSnapshot(); var promise = adapter.findBelongsTo(store, snapshot, link, relationship); - var serializer = serializerForAdapter(store, adapter, relationship.type); + var serializer = serializerForAdapter(store, adapter, modelName); var label = "DS: Handle Adapter#findBelongsTo of " + record + " : " + relationship.type; promise = Promise.cast(promise, label); @@ -103,7 +107,7 @@ export function _findBelongsTo(adapter, store, record, link, relationship) { return null; } - var record = store.push(relationship.type, payload); + var record = store.push(modelName, payload); return record; }); }, null, "DS: Extract payload of " + record + " : " + relationship.type); @@ -111,7 +115,8 @@ export function _findBelongsTo(adapter, store, record, link, relationship) { export function _findAll(adapter, store, typeClass, sinceToken) { var promise = adapter.findAll(store, typeClass, sinceToken); - var serializer = serializerForAdapter(store, adapter, typeClass); + var modelName = typeClass.modelName; + var serializer = serializerForAdapter(store, adapter, modelName); var label = "DS: Handle Adapter#findAll of " + typeClass; promise = Promise.cast(promise, label); @@ -123,17 +128,18 @@ export function _findAll(adapter, store, typeClass, sinceToken) { Ember.assert("The response from a findAll must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array'); - store.pushMany(typeClass, payload); + store.pushMany(modelName, payload); }); store.didUpdateAll(typeClass); - return store.all(typeClass); + return store.all(modelName); }, null, "DS: Extract payload of findAll " + typeClass); } export function _findQuery(adapter, store, typeClass, query, recordArray) { + var modelName = typeClass.modelName; var promise = adapter.findQuery(store, typeClass, query, recordArray); - var serializer = serializerForAdapter(store, adapter, typeClass); + var serializer = serializerForAdapter(store, adapter, modelName); var label = "DS: Handle Adapter#findQuery of " + typeClass; promise = Promise.cast(promise, label); diff --git a/packages/ember-data/tests/integration/adapter/build-url-mixin-test.js b/packages/ember-data/tests/integration/adapter/build-url-mixin-test.js index fa5ea293f03..cdc8008f614 100644 --- a/packages/ember-data/tests/integration/adapter/build-url-mixin-test.js +++ b/packages/ember-data/tests/integration/adapter/build-url-mixin-test.js @@ -28,6 +28,10 @@ module("integration/adapter/build-url-mixin - BuildURLMixin with RESTAdapter", { store = env.store; adapter = env.adapter; + Post = store.modelFor('post'); + Comment = store.modelFor('comment'); + SuperUser = store.modelFor('super-user'); + passedUrl = null; } }); @@ -154,7 +158,7 @@ test('buildURL - with camelized names', function() { ajaxResponse({ superUsers: [{ id: 1 }] }); run(function() { - store.find('superUser', 1).then(async(function(post) { + store.find('super-user', 1).then(async(function(post) { equal(passedUrl, "/super_users/1"); })); }); diff --git a/packages/ember-data/tests/integration/adapter/find-all-test.js b/packages/ember-data/tests/integration/adapter/find-all-test.js index b870cd536e1..a0541631765 100644 --- a/packages/ember-data/tests/integration/adapter/find-all-test.js +++ b/packages/ember-data/tests/integration/adapter/find-all-test.js @@ -1,6 +1,7 @@ var get = Ember.get; var Person, store, allRecords; var run = Ember.run; +var env; module("integration/adapter/find_all - Finding All Records of a Type", { setup: function() { @@ -12,6 +13,11 @@ module("integration/adapter/find_all - Finding All Records of a Type", { }); allRecords = null; + + env = setupStore({ + person: Person + }); + store = env.store; }, teardown: function() { @@ -25,20 +31,19 @@ module("integration/adapter/find_all - Finding All Records of a Type", { test("When all records for a type are requested, the store should call the adapter's `findAll` method.", function() { expect(5); - store = createStore({ adapter: DS.Adapter.extend({ - findAll: function(store, type, since) { - // this will get called twice - ok(true, "the adapter's findAll method should be invoked"); + env.registry.register('adapter:person', DS.Adapter.extend({ + findAll: function(store, type, since) { + // this will get called twice + ok(true, "the adapter's findAll method should be invoked"); - return Ember.RSVP.resolve([{ id: 1, name: "Braaaahm Dale" }]); - } - }) - }); + return Ember.RSVP.resolve([{ id: 1, name: "Braaaahm Dale" }]); + } + })); var allRecords; run(function() { - store.find(Person).then(function(all) { + store.find('person').then(function(all) { allRecords = all; equal(get(all, 'length'), 1, "the record array's length is 1 after a record is loaded into it"); equal(all.objectAt(0).get('name'), "Braaaahm Dale", "the first item in the record array is Braaaahm Dale"); @@ -46,7 +51,7 @@ test("When all records for a type are requested, the store should call the adapt }); run(function() { - store.find(Person).then(function(all) { + store.find('person').then(function(all) { // Only one record array per type should ever be created (identity map) strictEqual(allRecords, all, "the same record array is returned every time all records of a type are requested"); }); @@ -57,27 +62,25 @@ test("When all records for a type are requested, a rejection should reject the p expect(5); var count = 0; - store = createStore({ - adapter: DS.Adapter.extend({ - findAll: function(store, type, since) { - // this will get called twice - ok(true, "the adapter's findAll method should be invoked"); - - if (count++ === 0) { - return Ember.RSVP.reject(); - } else { - return Ember.RSVP.resolve([{ id: 1, name: "Braaaahm Dale" }]); - } + env.registry.register('adapter:person', DS.Adapter.extend({ + findAll: function(store, type, since) { + // this will get called twice + ok(true, "the adapter's findAll method should be invoked"); + + if (count++ === 0) { + return Ember.RSVP.reject(); + } else { + return Ember.RSVP.resolve([{ id: 1, name: "Braaaahm Dale" }]); } - }) - }); + } + })); var allRecords; run(function() { - store.find(Person).then(null, function() { + store.find('person').then(null, function() { ok(true, "The rejection should get here"); - return store.find(Person); + return store.find('person'); }).then(function(all) { allRecords = all; equal(get(all, 'length'), 1, "the record array's length is 1 after a record is loaded into it"); @@ -88,16 +91,15 @@ test("When all records for a type are requested, a rejection should reject the p test("When all records for a type are requested, records that are already loaded should be returned immediately.", function() { expect(3); - store = createStore({ adapter: DS.Adapter.extend() }); run(function() { // Load a record from the server - store.push(Person, { id: 1, name: "Jeremy Ashkenas" }); + store.push('person', { id: 1, name: "Jeremy Ashkenas" }); // Create a new, unsaved record in the store - store.createRecord(Person, { name: "Alex MacCaw" }); + store.createRecord('person', { name: "Alex MacCaw" }); }); - allRecords = store.all(Person); + allRecords = store.all('person'); equal(get(allRecords, 'length'), 2, "the record array's length is 2"); equal(allRecords.objectAt(0).get('name'), "Jeremy Ashkenas", "the first item in the record array is Jeremy Ashkenas"); @@ -107,14 +109,12 @@ test("When all records for a type are requested, records that are already loaded test("When all records for a type are requested, records that are created on the client should be added to the record array.", function() { expect(3); - store = createStore({ adapter: DS.Adapter.extend() }); - - allRecords = store.all(Person); + allRecords = store.all('person'); equal(get(allRecords, 'length'), 0, "precond - the record array's length is zero before any records are loaded"); run(function() { - store.createRecord(Person, { name: "Carsten Nielsen" }); + store.createRecord('person', { name: "Carsten Nielsen" }); }); equal(get(allRecords, 'length'), 1, "the record array's length is 1"); diff --git a/packages/ember-data/tests/integration/adapter/find-test.js b/packages/ember-data/tests/integration/adapter/find-test.js index 8d76e27f496..d07614e0ed4 100644 --- a/packages/ember-data/tests/integration/adapter/find-test.js +++ b/packages/ember-data/tests/integration/adapter/find-test.js @@ -1,4 +1,4 @@ -var Person, store; +var Person, store, env; var run = Ember.run; module("integration/adapter/find - Finding Records", { @@ -9,6 +9,11 @@ module("integration/adapter/find - Finding Records", { firstName: DS.attr('string'), lastName: DS.attr('string') }); + + env = setupStore({ + person: Person + }); + store = env.store; }, teardown: function() { @@ -17,7 +22,6 @@ module("integration/adapter/find - Finding Records", { }); test("It raises an assertion when no type is passed", function() { - store = createStore(); expectAssertion(function() { store.find(); @@ -25,14 +29,13 @@ test("It raises an assertion when no type is passed", function() { }); test("It raises an assertion when `undefined` is passed as id (#1705)", function() { - store = createStore(); expectAssertion(function() { - store.find(Person, undefined); + store.find('person', undefined); }, "You may not pass `undefined` as id to the store's find method"); expectAssertion(function() { - store.find(Person, null); + store.find('person', null); }, "You may not pass `null` as id to the store's find method"); }); @@ -41,35 +44,33 @@ test("When a single record is requested, the adapter's find method should be cal var count = 0; - store = createStore({ adapter: DS.Adapter.extend({ - find: function(store, type, id, snapshot) { - equal(type, Person, "the find method is called with the correct type"); - equal(count, 0, "the find method is only called once"); + env.registry.register('adapter:person', DS.Adapter.extend({ + find: function(store, type, id, snapshot) { + equal(type, Person, "the find method is called with the correct type"); + equal(count, 0, "the find method is only called once"); - count++; - return { id: 1, name: "Braaaahm Dale" }; - } - }) - }); + count++; + return { id: 1, name: "Braaaahm Dale" }; + } + })); run(function() { - store.find(Person, 1); - store.find(Person, 1); + store.find('person', 1); + store.find('person', 1); }); }); test("When a single record is requested multiple times, all .find() calls are resolved after the promise is resolved", function() { var deferred = Ember.RSVP.defer(); - store = createStore({ adapter: DS.Adapter.extend({ - find: function(store, type, id, snapshot) { - return deferred.promise; - } - }) - }); + env.registry.register('adapter:person', DS.Adapter.extend({ + find: function(store, type, id, snapshot) { + return deferred.promise; + } + })); run(function() { - store.find(Person, 1).then(async(function(person) { + store.find('person', 1).then(async(function(person) { equal(person.get('id'), "1"); equal(person.get('name'), "Braaaahm Dale"); @@ -85,7 +86,7 @@ test("When a single record is requested multiple times, all .find() calls are re }); run(function() { - store.find(Person, 1).then(async(function(post) { + store.find('person', 1).then(async(function(post) { equal(post.get('id'), "1"); equal(post.get('name'), "Braaaahm Dale"); @@ -107,15 +108,14 @@ test("When a single record is requested multiple times, all .find() calls are re }); test("When a single record is requested, and the promise is rejected, .find() is rejected.", function() { - store = createStore({ adapter: DS.Adapter.extend({ - find: function(store, type, id, snapshot) { - return Ember.RSVP.reject(); - } - }) - }); + env.registry.register('adapter:person', DS.Adapter.extend({ + find: function(store, type, id, snapshot) { + return Ember.RSVP.reject(); + } + })); run(function() { - store.find(Person, 1).then(null, async(function(reason) { + store.find('person', 1).then(null, async(function(reason) { ok(true, "The rejection handler was called"); })); }); @@ -124,18 +124,17 @@ test("When a single record is requested, and the promise is rejected, .find() is test("When a single record is requested, and the promise is rejected, the record should be unloaded.", function() { expect(2); - store = createStore({ adapter: DS.Adapter.extend({ - find: function(store, type, id, snapshot) { - return Ember.RSVP.reject(); - } - }) - }); + env.registry.register('adapter:person', DS.Adapter.extend({ + find: function(store, type, id, snapshot) { + return Ember.RSVP.reject(); + } + })); run(function() { - store.find(Person, 1).then(null, async(function(reason) { + store.find('person', 1).then(null, async(function(reason) { ok(true, "The rejection handler was called"); })); }); - ok(!store.hasRecordForId(Person, 1), "The record has been unloaded"); + ok(!store.hasRecordForId('person', 1), "The record has been unloaded"); }); diff --git a/packages/ember-data/tests/integration/adapter/store-adapter-test.js b/packages/ember-data/tests/integration/adapter/store-adapter-test.js index 50e854e3eb1..605b13c9f1a 100644 --- a/packages/ember-data/tests/integration/adapter/store-adapter-test.js +++ b/packages/ember-data/tests/integration/adapter/store-adapter-test.js @@ -664,7 +664,7 @@ test("if a updated record is marked as erred by the server, it enters an error s }; var person = run(function() { - return store.push(Person, { id: 1, name: "John Doe" }); + return store.push('person', { id: 1, name: "John Doe" }); }); run(store, 'find', 'person', 1).then(async(function(record) { diff --git a/packages/ember-data/tests/integration/debug-adapter-test.js b/packages/ember-data/tests/integration/debug-adapter-test.js index 3471c665065..53ec9a313ab 100644 --- a/packages/ember-data/tests/integration/debug-adapter-test.js +++ b/packages/ember-data/tests/integration/debug-adapter-test.js @@ -15,6 +15,9 @@ module("DS.DebugAdapter", { App.Post = DS.Model.extend({ title: DS.attr('string') }); + App.Post.reopenClass({ + modelName: 'post' + }); }); @@ -23,7 +26,7 @@ module("DS.DebugAdapter", { debugAdapter.reopen({ getModelTypes: function() { - return Ember.A([{ klass: App.Post, name: 'App.Post' }]); + return Ember.A([{ klass: App.__container__.lookupFactory('model:post'), name: 'App.Post' }]); } }); }, @@ -39,7 +42,7 @@ test("Watching Model Types", function() { equal(types.length, 1); equal(types[0].name, 'App.Post'); equal(types[0].count, 0); - strictEqual(types[0].object, App.Post); + strictEqual(types[0].object, store.modelFor('post')); }; var updated = function(types) { @@ -71,7 +74,7 @@ test("Watching Records", function() { removedCount = count; }; - debugAdapter.watchRecords(App.Post, recordsAdded, recordsUpdated, recordsRemoved); + debugAdapter.watchRecords(App.__container__.lookupFactory('model:post'), recordsAdded, recordsUpdated, recordsRemoved); equal(get(addedRecords, 'length'), 1); record = addedRecords[0]; diff --git a/packages/ember-data/tests/integration/filter-test.js b/packages/ember-data/tests/integration/filter-test.js index 8196c4ff526..e59721e90ee 100644 --- a/packages/ember-data/tests/integration/filter-test.js +++ b/packages/ember-data/tests/integration/filter-test.js @@ -193,7 +193,7 @@ test("a Record Array can update its filter", function() { asyncBryn = store.find('person', 3); }); - store.filter(Person, function(hash) { + store.filter('person', function(hash) { if (hash.get('name').match(/Scumbag [KD]/)) { return true; } }).then(async(function(recordArray) { @@ -253,7 +253,7 @@ test("a Record Array can update its filter and notify array observers", function asyncBryn = store.find('person', 3); }); - store.filter(Person, function(hash) { + store.filter('person', function(hash) { if (hash.get('name').match(/Scumbag [KD]/)) { return true; } }).then(async(function(recordArray) { @@ -378,7 +378,7 @@ test("filter with query persists query on the resulting filteredRecordArray", fu var filter; run(function() { - filter = store.filter(Person, { foo: 1 }, function(person) { + filter = store.filter('person', { foo: 1 }, function(person) { return true; }); }); @@ -400,7 +400,7 @@ test("it is possible to filter by state flags", function() { } })); - filter = store.filter(Person, function(person) { + filter = store.filter('person', function(person) { return person.get('isLoaded'); }); }); diff --git a/packages/ember-data/tests/integration/inverse-test.js b/packages/ember-data/tests/integration/inverse-test.js index 107adffc71c..e55f15ec4b9 100644 --- a/packages/ember-data/tests/integration/inverse-test.js +++ b/packages/ember-data/tests/integration/inverse-test.js @@ -26,7 +26,7 @@ module('integration/inverse_test - inverseFor', { Job.toString = stringify('job'); ReflexiveModel = DS.Model.extend({ - reflexiveProp: belongsTo('reflexiveModel') + reflexiveProp: belongsTo('reflexive-model') }); ReflexiveModel.toString = stringify('reflexiveModel'); @@ -143,7 +143,7 @@ test("Errors out if you do not define an inverse for a reflexive relationship", warns(function() { var reflexiveModel; run(function() { - reflexiveModel = store.push('reflexiveModel', { id: 1 }); + reflexiveModel = store.push('reflexive-model', { id: 1 }); }); }, /Detected a reflexive relationship by the name of 'reflexiveProp'/); }); diff --git a/packages/ember-data/tests/integration/lifecycle-hooks-test.js b/packages/ember-data/tests/integration/lifecycle-hooks-test.js index dab3da4c7a6..3625e35c9e0 100644 --- a/packages/ember-data/tests/integration/lifecycle-hooks-test.js +++ b/packages/ember-data/tests/integration/lifecycle-hooks-test.js @@ -28,7 +28,7 @@ asyncTest("When the adapter acknowledges that a record has been created, a `didC var person; run(function() { - person = env.store.createRecord(Person, { name: "Yehuda Katz" }); + person = env.store.createRecord('person', { name: "Yehuda Katz" }); }); person.on('didCreate', function() { @@ -50,7 +50,7 @@ test("When the adapter acknowledges that a record has been created without a new var person; run(function() { - person = env.store.createRecord(Person, { id: 99, name: "Yehuda Katz" }); + person = env.store.createRecord('person', { id: 99, name: "Yehuda Katz" }); }); person.on('didCreate', function() { diff --git a/packages/ember-data/tests/integration/multiple_stores_test.js b/packages/ember-data/tests/integration/multiple_stores_test.js index 09210a6db6b..badc61087e4 100644 --- a/packages/ember-data/tests/integration/multiple_stores_test.js +++ b/packages/ember-data/tests/integration/multiple_stores_test.js @@ -7,15 +7,15 @@ module("integration/multiple_stores - Multiple Stores Tests", { SuperVillain = DS.Model.extend({ firstName: DS.attr('string'), lastName: DS.attr('string'), - homePlanet: DS.belongsTo("homePlanet", { inverse: 'villains' }), - evilMinions: DS.hasMany("evilMinion") + homePlanet: DS.belongsTo('home-planet', { inverse: 'villains' }), + evilMinions: DS.hasMany('evil-minion') }); HomePlanet = DS.Model.extend({ name: DS.attr('string'), - villains: DS.hasMany('superVillain', { inverse: 'homePlanet' }) + villains: DS.hasMany('super-villain', { inverse: 'homePlanet' }) }); EvilMinion = DS.Model.extend({ - superVillain: DS.belongsTo('superVillain'), + superVillain: DS.belongsTo('super-villain'), name: DS.attr('string') }); @@ -77,9 +77,9 @@ test("embedded records should be created in multiple stores", function() { } })); - var serializer_main = env.store.serializerFor("homePlanet"); - var serializer_a = env.store_a.serializerFor("homePlanet"); - var serializer_b = env.store_b.serializerFor("homePlanet"); + var serializer_main = env.store.serializerFor('home-planet'); + var serializer_a = env.store_a.serializerFor('home-planet'); + var serializer_b = env.store_b.serializerFor('home-planet'); var json_hash_main = { home_planet: { @@ -117,18 +117,18 @@ test("embedded records should be created in multiple stores", function() { var json_main, json_a, json_b; run(function() { - json_main = serializer_main.extractSingle(env.store, HomePlanet, json_hash_main); - equal(env.store.hasRecordForId("superVillain", "1"), true, "superVillain should exist in store:main"); + json_main = serializer_main.extractSingle(env.store, env.store.modelFor('home-planet'), json_hash_main); + equal(env.store.hasRecordForId('super-villain', "1"), true, "superVillain should exist in store:main"); }); run(function() { - json_a = serializer_a.extractSingle(env.store_a, HomePlanet, json_hash_a); - equal(env.store_a.hasRecordForId("superVillain", "1"), true, "superVillain should exist in store:store-a"); + json_a = serializer_a.extractSingle(env.store_a, env.store_a.modelFor('home-planet'), json_hash_a); + equal(env.store_a.hasRecordForId("super-villain", "1"), true, "superVillain should exist in store:store-a"); }); run(function() { - json_b = serializer_b.extractSingle(env.store_b, HomePlanet, json_hash_b); - equal(env.store_b.hasRecordForId("superVillain", "1"), true, "superVillain should exist in store:store-b"); + json_b = serializer_b.extractSingle(env.store_b, env.store_a.modelFor('home-planet'), json_hash_b); + equal(env.store_b.hasRecordForId("super-villain", "1"), true, "superVillain should exist in store:store-b"); }); }); diff --git a/packages/ember-data/tests/integration/records/load-test.js b/packages/ember-data/tests/integration/records/load-test.js index 368e4641ff4..b13d373b196 100644 --- a/packages/ember-data/tests/integration/records/load-test.js +++ b/packages/ember-data/tests/integration/records/load-test.js @@ -27,13 +27,13 @@ test("When loading a record fails, the isLoading is set to false", function() { }; run(function() { - env.store.find('post', 1).then(null, function() { + env.store.find('post', 1).then(null, async(function() { // store.recordForId is private, but there is currently no other way to // get the specific record instance, since it is not passed to this // rejection handler var post = env.store.recordForId('post', 1); equal(post.get("isLoading"), false, "post is not loading anymore"); - }); + })); }); }); diff --git a/packages/ember-data/tests/integration/relationships/belongs-to-test.js b/packages/ember-data/tests/integration/relationships/belongs-to-test.js index a906968a355..9679be315a2 100644 --- a/packages/ember-data/tests/integration/relationships/belongs-to-test.js +++ b/packages/ember-data/tests/integration/relationships/belongs-to-test.js @@ -380,7 +380,7 @@ test("relationshipsByName does not cache a factory", function() { // A model is looked up in the store based on a string, via user input var messageModelFromStore = store.modelFor('message'); // And the model is lookup up internally via the relationship type - var messageModelFromRelationType = store.modelFor(messageType); + var messageModelFromRelationType = store.modelFor(messageType.modelName); equal(messageModelFromRelationType, messageModelFromStore, "model factory based on relationship type matches the model based on store.modelFor"); diff --git a/packages/ember-data/tests/integration/relationships/polymorphic-mixins-belongs-to-test.js b/packages/ember-data/tests/integration/relationships/polymorphic-mixins-belongs-to-test.js index 261517873e2..f3977b596b2 100644 --- a/packages/ember-data/tests/integration/relationships/polymorphic-mixins-belongs-to-test.js +++ b/packages/ember-data/tests/integration/relationships/polymorphic-mixins-belongs-to-test.js @@ -90,7 +90,7 @@ test("Setting the polymorphic belongsTo with an object that does not implement t var user, video; run(function() { user = store.push('user', { id: 1, name: 'Stanley' }); - video = store.push('notMessage', { id: 2, video: 'Here comes Youtube' }); + video = store.push('not-message', { id: 2, video: 'Here comes Youtube' }); }); run(function() { @@ -135,7 +135,7 @@ test("Setting the polymorphic belongsTo with an object that does not implement t var user, video; run(function() { user = store.push('user', { id: 1, name: 'Stanley' }); - video = store.push('notMessage', { id: 2, video: 'Here comes Youtube' }); + video = store.push('not-message', { id: 2, video: 'Here comes Youtube' }); }); run(function() { diff --git a/packages/ember-data/tests/integration/relationships/polymorphic-mixins-has-many-test.js b/packages/ember-data/tests/integration/relationships/polymorphic-mixins-has-many-test.js index 3d84bb2f642..5cd8d528b60 100644 --- a/packages/ember-data/tests/integration/relationships/polymorphic-mixins-has-many-test.js +++ b/packages/ember-data/tests/integration/relationships/polymorphic-mixins-has-many-test.js @@ -93,7 +93,7 @@ test("Pushing a an object that does not implement the mixin to the mixin accepti var user,notMessage; run(function() { user = store.push('user', { id: 1, name: 'Stanley', messages: [] }); - notMessage = store.push('notMessage', { id: 2, video: 'Here comes Youtube' }); + notMessage = store.push('not-message', { id: 2, video: 'Here comes Youtube' }); }); run(function() { @@ -140,7 +140,7 @@ test("Pushing a an object that does not implement the mixin to the mixin accepti var user,notMessage; run(function() { user = store.push('user', { id: 1, name: 'Stanley', messages: [] }); - notMessage = store.push('notMessage', { id: 2, video: 'Here comes Youtube' }); + notMessage = store.push('not-message', { id: 2, video: 'Here comes Youtube' }); }); run(function() { diff --git a/packages/ember-data/tests/integration/serializers/embedded-records-mixin-test.js b/packages/ember-data/tests/integration/serializers/embedded-records-mixin-test.js index 8606df6b8a8..0c4f1e99a71 100644 --- a/packages/ember-data/tests/integration/serializers/embedded-records-mixin-test.js +++ b/packages/ember-data/tests/integration/serializers/embedded-records-mixin-test.js @@ -10,32 +10,32 @@ module("integration/embedded_records_mixin - EmbeddedRecordsMixin", { SuperVillain = DS.Model.extend({ firstName: DS.attr('string'), lastName: DS.attr('string'), - homePlanet: DS.belongsTo("homePlanet", { inverse: 'villains' }), - secretLab: DS.belongsTo("secretLab"), - secretWeapons: DS.hasMany("secretWeapon"), - evilMinions: DS.hasMany("evilMinion") + homePlanet: DS.belongsTo('home-planet', { inverse: 'villains' }), + secretLab: DS.belongsTo('secret-lab'), + secretWeapons: DS.hasMany('secret-weapon'), + evilMinions: DS.hasMany('evil-minion') }); HomePlanet = DS.Model.extend({ name: DS.attr('string'), - villains: DS.hasMany('superVillain', { inverse: 'homePlanet' }) + villains: DS.hasMany('super-villain', { inverse: 'homePlanet' }) }); SecretLab = DS.Model.extend({ minionCapacity: DS.attr('number'), vicinity: DS.attr('string'), - superVillain: DS.belongsTo('superVillain') + superVillain: DS.belongsTo('super-villain') }); BatCave = SecretLab.extend({ infiltrated: DS.attr('boolean') }); SecretWeapon = DS.Model.extend({ name: DS.attr('string'), - superVillain: DS.belongsTo('superVillain') + superVillain: DS.belongsTo('super-villain') }); LightSaber = SecretWeapon.extend({ color: DS.attr('string') }); EvilMinion = DS.Model.extend({ - superVillain: DS.belongsTo('superVillain'), + superVillain: DS.belongsTo('super-villain'), name: DS.attr('string') }); Comment = DS.Model.extend({ @@ -53,13 +53,13 @@ module("integration/embedded_records_mixin - EmbeddedRecordsMixin", { evilMinion: EvilMinion, comment: Comment }); - env.store.modelFor('superVillain'); - env.store.modelFor('homePlanet'); - env.store.modelFor('secretLab'); - env.store.modelFor('batCave'); - env.store.modelFor('secretWeapon'); - env.store.modelFor('lightSaber'); - env.store.modelFor('evilMinion'); + env.store.modelFor('super-villain'); + env.store.modelFor('home-planet'); + env.store.modelFor('secret-lab'); + env.store.modelFor('bat-cave'); + env.store.modelFor('secret-weapon'); + env.store.modelFor('light-saber'); + env.store.modelFor('evil-minion'); env.store.modelFor('comment'); env.registry.register('serializer:application', DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin)); env.registry.register('serializer:-active-model', DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin)); @@ -105,7 +105,7 @@ test("extractSingle with embedded objects", function() { villains: ["1"] }); run(function() { - env.store.find("superVillain", 1).then(function(minion) { + env.store.find('super-villain', 1).then(function(minion) { equal(minion.get('firstName'), "Tom"); }); }); @@ -152,14 +152,14 @@ test("extractSingle with embedded objects inside embedded objects", function() { villains: ["1"] }); run(function() { - env.store.find("superVillain", 1).then(async(function(villain) { + env.store.find('super-villain', 1).then(function(villain) { equal(villain.get('firstName'), "Tom"); equal(villain.get('evilMinions.length'), 1, "Should load the embedded child"); equal(villain.get('evilMinions.firstObject.name'), "Alex", "Should load the embedded child"); - })); - env.store.find("evilMinion", 1).then(async(function(minion) { + }); + env.store.find('evil-minion', 1).then(function(minion) { equal(minion.get('name'), "Alex"); - })); + }); }); }); @@ -200,8 +200,8 @@ test("extractSingle with embedded objects of same type", function() { root: true, children: ["2", "3"] }, "Primary record was correct"); - equal(env.store.recordForId("comment", "2").get("body"), "World", "Secondary records found in the store"); - equal(env.store.recordForId("comment", "3").get("body"), "Foo", "Secondary records found in the store"); + equal(env.store.recordForId('comment', "2").get("body"), "World", "Secondary records found in the store"); + equal(env.store.recordForId('comment', "3").get("body"), "Foo", "Secondary records found in the store"); }); test("extractSingle with embedded objects inside embedded objects of same type", function() { @@ -246,16 +246,16 @@ test("extractSingle with embedded objects inside embedded objects of same type", root: true, children: ["2", "3"] }, "Primary record was correct"); - equal(env.store.recordForId("comment", "2").get("body"), "World", "Secondary records found in the store"); - equal(env.store.recordForId("comment", "3").get("body"), "Foo", "Secondary records found in the store"); - equal(env.store.recordForId("comment", "4").get("body"), "Another", "Secondary records found in the store"); - equal(env.store.recordForId("comment", "2").get("children.length"), 1, "Should have one embedded record"); - equal(env.store.recordForId("comment", "2").get("children.firstObject.body"), "Another", "Should have one embedded record"); + equal(env.store.recordForId('comment', "2").get("body"), "World", "Secondary records found in the store"); + equal(env.store.recordForId('comment', "3").get("body"), "Foo", "Secondary records found in the store"); + equal(env.store.recordForId('comment', "4").get("body"), "Another", "Secondary records found in the store"); + equal(env.store.recordForId('comment', "2").get("children.length"), 1, "Should have one embedded record"); + equal(env.store.recordForId('comment', "2").get("children.firstObject.body"), "Another", "Should have one embedded record"); }); test("extractSingle with embedded objects of same type, but from separate attributes", function() { HomePlanet.reopen({ - reformedVillains: DS.hasMany('superVillain', { inverse: null }) + reformedVillains: DS.hasMany('super-villain', { inverse: null }) }); env.registry.register('adapter:home-planet', DS.ActiveModelAdapter); @@ -299,10 +299,10 @@ test("extractSingle with embedded objects of same type, but from separate attrib reformedVillains: ["2", "4"] }, "Primary hash was correct"); - equal(env.store.recordForId("superVillain", "1").get("firstName"), "Tom", "Secondary records found in the store"); - equal(env.store.recordForId("superVillain", "2").get("firstName"), "Alex", "Secondary records found in the store"); - equal(env.store.recordForId("superVillain", "3").get("firstName"), "Yehuda", "Secondary records found in the store"); - equal(env.store.recordForId("superVillain", "4").get("firstName"), "Erik", "Secondary records found in the store"); + equal(env.store.recordForId('super-villain', "1").get("firstName"), "Tom", "Secondary records found in the store"); + equal(env.store.recordForId('super-villain', "2").get("firstName"), "Alex", "Secondary records found in the store"); + equal(env.store.recordForId('super-villain', "3").get("firstName"), "Yehuda", "Secondary records found in the store"); + equal(env.store.recordForId('super-villain', "4").get("firstName"), "Erik", "Secondary records found in the store"); }); test("extractArray with embedded objects", function() { @@ -339,7 +339,7 @@ test("extractArray with embedded objects", function() { }]); run(function() { - env.store.find("superVillain", 1).then(function(minion) { + env.store.find('super-villain', 1).then(function(minion) { equal(minion.get('firstName'), "Tom"); }); }); @@ -383,8 +383,8 @@ test("extractArray with embedded objects with custom primary key", function() { }]); run(function() { - return env.store.find("superVillain", 1).then(function(minion) { - env.registry.unregister('serializer:super-villain'); + return env.store.find('super-villain', 1).then(function(minion) { + env.registry.unregister('serializer:superVillain'); equal(minion.get('firstName'), "Alex"); }); }); @@ -428,7 +428,7 @@ test("extractArray with embedded objects with identical relationship and attribu }]); run(function() { - env.store.find("superVillain", 1).then(function(minion) { + env.store.find('super-villain', 1).then(function(minion) { equal(minion.get('firstName'), "Alex"); }); }); @@ -473,13 +473,13 @@ test("extractArray with embedded objects of same type as primary type", function children: ["2", "3"] }], "Primary array is correct"); - equal(env.store.recordForId("comment", "2").get("body"), "World", "Secondary record found in the store"); - equal(env.store.recordForId("comment", "3").get("body"), "Foo", "Secondary record found in the store"); + equal(env.store.recordForId('comment', "2").get("body"), "World", "Secondary record found in the store"); + equal(env.store.recordForId('comment', "3").get("body"), "Foo", "Secondary record found in the store"); }); test("extractArray with embedded objects of same type, but from separate attributes", function() { HomePlanet.reopen({ - reformedVillains: DS.hasMany('superVillain') + reformedVillains: DS.hasMany('super-villain') }); env.registry.register('adapter:home-planet', DS.ActiveModelAdapter); @@ -545,18 +545,18 @@ test("extractArray with embedded objects of same type, but from separate attribu reformedVillains: ["5", "6"] }], "Primary array was correct"); - equal(env.store.recordForId("superVillain", "1").get("firstName"), "Tom", "Secondary records found in the store"); - equal(env.store.recordForId("superVillain", "2").get("firstName"), "Alex", "Secondary records found in the store"); - equal(env.store.recordForId("superVillain", "3").get("firstName"), "Yehuda", "Secondary records found in the store"); - equal(env.store.recordForId("superVillain", "4").get("firstName"), "Erik", "Secondary records found in the store"); - equal(env.store.recordForId("superVillain", "5").get("firstName"), "Peter", "Secondary records found in the store"); - equal(env.store.recordForId("superVillain", "6").get("firstName"), "Trek", "Secondary records found in the store"); + equal(env.store.recordForId('super-villain', "1").get("firstName"), "Tom", "Secondary records found in the store"); + equal(env.store.recordForId('super-villain', "2").get("firstName"), "Alex", "Secondary records found in the store"); + equal(env.store.recordForId('super-villain', "3").get("firstName"), "Yehuda", "Secondary records found in the store"); + equal(env.store.recordForId('super-villain', "4").get("firstName"), "Erik", "Secondary records found in the store"); + equal(env.store.recordForId('super-villain', "5").get("firstName"), "Peter", "Secondary records found in the store"); + equal(env.store.recordForId('super-villain', "6").get("firstName"), "Trek", "Secondary records found in the store"); }); test("serialize supports serialize:false on non-relationship properties", function() { var tom; run(function() { - tom = env.store.createRecord(SuperVillain, { firstName: "Tom", lastName: "Dale", id: '1' }); + tom = env.store.createRecord('super-villain', { firstName: "Tom", lastName: "Dale", id: '1' }); }); env.registry.register('serializer:super-villain', DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, { @@ -580,8 +580,8 @@ test("serialize supports serialize:false on non-relationship properties", functi test("serialize with embedded objects (hasMany relationship)", function() { var tom, league; run(function() { - league = env.store.createRecord(HomePlanet, { name: "Villain League", id: "123" }); - tom = env.store.createRecord(SuperVillain, { firstName: "Tom", lastName: "Dale", homePlanet: league, id: '1' }); + league = env.store.createRecord('home-planet', { name: "Villain League", id: "123" }); + tom = env.store.createRecord('super-villain', { firstName: "Tom", lastName: "Dale", homePlanet: league, id: '1' }); }); env.registry.register('serializer:home-planet', DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, { @@ -611,8 +611,8 @@ test("serialize with embedded objects (hasMany relationship)", function() { test("serialize with embedded objects (hasMany relationship) supports serialize:false", function() { run(function() { - league = env.store.createRecord(HomePlanet, { name: "Villain League", id: "123" }); - env.store.createRecord(SuperVillain, { firstName: "Tom", lastName: "Dale", homePlanet: league, id: '1' }); + league = env.store.createRecord('home-planet', { name: "Villain League", id: "123" }); + env.store.createRecord('super-villain', { firstName: "Tom", lastName: "Dale", homePlanet: league, id: '1' }); }); env.registry.register('serializer:home-planet', DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, { @@ -634,8 +634,8 @@ test("serialize with embedded objects (hasMany relationship) supports serialize: test("serialize with (new) embedded objects (hasMany relationship)", function() { run(function() { - league = env.store.createRecord(HomePlanet, { name: "Villain League", id: "123" }); - env.store.createRecord(SuperVillain, { firstName: "Tom", lastName: "Dale", homePlanet: league }); + league = env.store.createRecord('home-planet', { name: "Villain League", id: "123" }); + env.store.createRecord('super-villain', { firstName: "Tom", lastName: "Dale", homePlanet: league }); }); env.registry.register('serializer:home-planet', DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, { @@ -662,9 +662,9 @@ test("serialize with (new) embedded objects (hasMany relationship)", function() test("serialize with embedded objects (hasMany relationships, including related objects not embedded)", function() { run(function() { - superVillain = env.store.createRecord(SuperVillain, { id: 1, firstName: "Super", lastName: "Villian" }); - evilMinion = env.store.createRecord(EvilMinion, { id: 1, name: "Evil Minion", superVillian: superVillain }); - secretWeapon = env.store.createRecord(SecretWeapon, { id: 1, name: "Secret Weapon", superVillain: superVillain }); + superVillain = env.store.createRecord('super-villain', { id: 1, firstName: "Super", lastName: "Villian" }); + evilMinion = env.store.createRecord('evil-minion', { id: 1, name: "Evil Minion", superVillian: superVillain }); + secretWeapon = env.store.createRecord('secret-weapon', { id: 1, name: "Secret Weapon", superVillain: superVillain }); superVillain.get('evilMinions').pushObject(evilMinion); superVillain.get('secretWeapons').pushObject(secretWeapon); }); @@ -738,7 +738,7 @@ test("extractSingle with embedded object (belongsTo relationship)", function() { }); run(function() { - env.store.find("secretLab", 101).then(function(secretLab) { + env.store.find('secret-lab', 101).then(function(secretLab) { equal(secretLab.get('id'), '101'); equal(secretLab.get('minionCapacity'), 5000); equal(secretLab.get('vicinity'), 'California, USA'); @@ -760,10 +760,10 @@ test("serialize with embedded object (belongsTo relationship)", function() { // records with an id, persisted tom = env.store.createRecord( - SuperVillain, + 'super-villain', { firstName: "Tom", lastName: "Dale", id: "1", - secretLab: env.store.createRecord(SecretLab, { minionCapacity: 5000, vicinity: "California, USA", id: "101" }), - homePlanet: env.store.createRecord(HomePlanet, { name: "Villain League", id: "123" }) + secretLab: env.store.createRecord('secret-lab', { minionCapacity: 5000, vicinity: "California, USA", id: "101" }), + homePlanet: env.store.createRecord('home-planet', { name: "Villain League", id: "123" }) } ); }); @@ -803,7 +803,7 @@ test("serialize with embedded object (belongsTo relationship) works with differe run(function() { tom = env.store.createRecord( - SuperVillain, + 'super-villain', { firstName: "Tom", lastName: "Dale", id: "1", secretLab: env.store.createRecord('secret-lab', { minionCapacity: 5000, vicinity: "California, USA", id: "101" }), homePlanet: env.store.createRecord('home-planet', { name: "Villain League", id: "123" }) @@ -842,10 +842,10 @@ test("serialize with embedded object (belongsTo relationship, new no id)", funct run(function() { tom = env.store.createRecord( - SuperVillain, + 'super-villain', { firstName: "Tom", lastName: "Dale", - secretLab: env.store.createRecord(SecretLab, { minionCapacity: 5000, vicinity: "California, USA" }), - homePlanet: env.store.createRecord(HomePlanet, { name: "Villain League", id: "123" }) + secretLab: env.store.createRecord('secret-lab', { minionCapacity: 5000, vicinity: "California, USA" }), + homePlanet: env.store.createRecord('home-planet', { name: "Villain League", id: "123" }) } ); }); @@ -879,10 +879,10 @@ test("serialize with embedded object (belongsTo relationship) supports serialize run(function() { tom = env.store.createRecord( - SuperVillain, + 'super-villain', { firstName: "Tom", lastName: "Dale", id: "1", - secretLab: env.store.createRecord(SecretLab, { minionCapacity: 5000, vicinity: "California, USA", id: "101" }), - homePlanet: env.store.createRecord(HomePlanet, { name: "Villain League", id: "123" }) + secretLab: env.store.createRecord('secret-lab', { minionCapacity: 5000, vicinity: "California, USA", id: "101" }), + homePlanet: env.store.createRecord('home-planet', { name: "Villain League", id: "123" }) } ); }); @@ -914,10 +914,10 @@ test("serialize with embedded object (belongsTo relationship) supports serialize run(function() { tom = env.store.createRecord( - SuperVillain, + 'super-villain', { firstName: "Tom", lastName: "Dale", id: "1", - secretLab: env.store.createRecord(SecretLab, { minionCapacity: 5000, vicinity: "California, USA", id: "101" }), - homePlanet: env.store.createRecord(HomePlanet, { name: "Villain League", id: "123" }) + secretLab: env.store.createRecord('secret-lab', { minionCapacity: 5000, vicinity: "California, USA", id: "101" }), + homePlanet: env.store.createRecord('home-planet', { name: "Villain League", id: "123" }) } ); }); @@ -949,10 +949,10 @@ test("serialize with embedded object (belongsTo relationship) supports serialize run(function() { tom = env.store.createRecord( - SuperVillain, + 'super-villain', { firstName: "Tom", lastName: "Dale", id: "1", - secretLab: env.store.createRecord('secretLab', { minionCapacity: 5000, vicinity: "California, USA", id: "101" }), - homePlanet: env.store.createRecord('homePlanet', { name: "Villain League", id: "123" }) + secretLab: env.store.createRecord('secret-lab', { minionCapacity: 5000, vicinity: "California, USA", id: "101" }), + homePlanet: env.store.createRecord('home-planet', { name: "Villain League", id: "123" }) } ); }); @@ -982,10 +982,10 @@ test("serialize with embedded object (belongsTo relationship) supports serialize var tom, json; run(function() { tom = env.store.createRecord( - SuperVillain, + 'super-villain', { firstName: "Tom", lastName: "Dale", id: "1", - secretLab: env.store.createRecord(SecretLab, { minionCapacity: 5000, vicinity: "California, USA", id: "101" }), - homePlanet: env.store.createRecord(HomePlanet, { name: "Villain League", id: "123" }) + secretLab: env.store.createRecord('secret-lab', { minionCapacity: 5000, vicinity: "California, USA", id: "101" }), + homePlanet: env.store.createRecord('home-planet', { name: "Villain League", id: "123" }) } ); }); @@ -1012,10 +1012,10 @@ test("serialize with embedded object (belongsTo relationship) serializes the id run(function() { tom = env.store.createRecord( - SuperVillain, + 'super-villain', { firstName: "Tom", lastName: "Dale", id: "1", - secretLab: env.store.createRecord(SecretLab, { minionCapacity: 5000, vicinity: "California, USA", id: "101" }), - homePlanet: env.store.createRecord(HomePlanet, { name: "Villain League", id: "123" }) + secretLab: env.store.createRecord('secret-lab', { minionCapacity: 5000, vicinity: "California, USA", id: "101" }), + homePlanet: env.store.createRecord('home-planet', { name: "Villain League", id: "123" }) } ); }); @@ -1044,9 +1044,9 @@ test("when related record is not present, serialize embedded record (with a belo run(function() { tom = env.store.createRecord( - SuperVillain, + 'super-villain', { firstName: "Tom", lastName: "Dale", id: "1", - homePlanet: env.store.createRecord(HomePlanet, { name: "Villain League", id: "123" }) + homePlanet: env.store.createRecord('home-planet', { name: "Villain League", id: "123" }) } ); }); @@ -1106,13 +1106,13 @@ test("extractSingle with multiply-nested belongsTo", function() { superVillain: "1" }, "Primary hash was correct"); - equal(env.store.recordForId("superVillain", "1").get("firstName"), "Tom", "Secondary record, Tom, found in the steore"); - equal(env.store.recordForId("homePlanet", "1").get("name"), "Umber", "Nested Secondary record, Umber, found in the store"); + equal(env.store.recordForId('super-villain', "1").get("firstName"), "Tom", "Secondary record, Tom, found in the steore"); + equal(env.store.recordForId('home-planet', "1").get("name"), "Umber", "Nested Secondary record, Umber, found in the store"); }); test("extractSingle with polymorphic hasMany", function() { SuperVillain.reopen({ - secretWeapons: DS.hasMany("secretWeapon", { polymorphic: true }) + secretWeapons: DS.hasMany('secret-weapon', { polymorphic: true }) }); env.registry.register('adapter:super-villain', DS.ActiveModelAdapter); @@ -1159,8 +1159,8 @@ test("extractSingle with polymorphic hasMany", function() { ] }, "Primary hash was correct"); - equal(env.store.recordForId("secretWeapon", "1").get("name"), "The Death Star", "Embedded polymorphic SecretWeapon found"); - equal(env.store.recordForId("lightSaber", "1").get("name"), "Tom's LightSaber", "Embedded polymorphic LightSaber found"); + equal(env.store.recordForId('secret-weapon', "1").get("name"), "The Death Star", "Embedded polymorphic SecretWeapon found"); + equal(env.store.recordForId('light-saber', "1").get("name"), "Tom's LightSaber", "Embedded polymorphic LightSaber found"); }); @@ -1169,7 +1169,7 @@ test("extractSingle with polymorphic belongsTo", function() { expect(2); SuperVillain.reopen({ - secretLab: DS.belongsTo("secretLab", { polymorphic: true }) + secretLab: DS.belongsTo('secret-lab', { polymorphic: true }) }); env.registry.register('adapter:super-villain', DS.ActiveModelAdapter); @@ -1207,20 +1207,20 @@ test("extractSingle with polymorphic belongsTo", function() { secretLabType: "bat-cave" }, "Primary has was correct"); - equal(env.store.recordForId("batCave", "1").get("infiltrated"), true, "Embedded polymorphic BatCave was found"); + equal(env.store.recordForId('bat-cave', "1").get("infiltrated"), true, "Embedded polymorphic BatCave was found"); }); test("Mixin can be used with RESTSerializer which does not define keyForAttribute", function() { run(function() { - homePlanet = env.store.createRecord(HomePlanet, { name: "Villain League", id: "123" }); - secretLab = env.store.createRecord(SecretLab, { minionCapacity: 5000, vicinity: "California, USA", id: "101" }); - superVillain = env.store.createRecord(SuperVillain, { + homePlanet = env.store.createRecord('home-planet', { name: "Villain League", id: "123" }); + secretLab = env.store.createRecord('secret-lab', { minionCapacity: 5000, vicinity: "California, USA", id: "101" }); + superVillain = env.store.createRecord('super-villain', { id: "1", firstName: "Super", lastName: "Villian", homePlanet: homePlanet, secretLab: secretLab }); - secretWeapon = env.store.createRecord(SecretWeapon, { id: "1", name: "Secret Weapon", superVillain: superVillain }); + secretWeapon = env.store.createRecord('secret-weapon', { id: "1", name: "Secret Weapon", superVillain: superVillain }); superVillain.get('secretWeapons').pushObject(secretWeapon); - evilMinion = env.store.createRecord(EvilMinion, { id: "1", name: "Evil Minion", superVillian: superVillain }); + evilMinion = env.store.createRecord('evil-minion', { id: "1", name: "Evil Minion", superVillian: superVillain }); superVillain.get('evilMinions').pushObject(evilMinion); }); @@ -1288,19 +1288,19 @@ test("normalize with custom belongsTo primary key", function() { superVillain: "1" }, "Primary hash was correct"); - equal(env.store.recordForId("superVillain", "1").get("firstName"), "Tom", "Secondary record, Tom, found in the steore"); + equal(env.store.recordForId('super-villain', "1").get("firstName"), "Tom", "Secondary record, Tom, found in the steore"); }); test("serializing relationships with an embedded and without calls super when not attr not present", function() { run(function() { - homePlanet = env.store.createRecord(HomePlanet, { name: "Villain League", id: "123" }); - secretLab = env.store.createRecord(SecretLab, { minionCapacity: 5000, vicinity: "California, USA", id: "101" }); - superVillain = env.store.createRecord(SuperVillain, { + homePlanet = env.store.createRecord('home-planet', { name: "Villain League", id: "123" }); + secretLab = env.store.createRecord('secret-lab', { minionCapacity: 5000, vicinity: "California, USA", id: "101" }); + superVillain = env.store.createRecord('super-villain', { id: "1", firstName: "Super", lastName: "Villian", homePlanet: homePlanet, secretLab: secretLab }); - secretWeapon = env.store.createRecord(SecretWeapon, { id: "1", name: "Secret Weapon", superVillain: superVillain }); + secretWeapon = env.store.createRecord('secret-weapon', { id: "1", name: "Secret Weapon", superVillain: superVillain }); superVillain.get('secretWeapons').pushObject(secretWeapon); - evilMinion = env.store.createRecord(EvilMinion, { id: "1", name: "Evil Minion", superVillian: superVillain }); + evilMinion = env.store.createRecord('evil-minion', { id: "1", name: "Evil Minion", superVillian: superVillain }); superVillain.get('evilMinions').pushObject(evilMinion); }); diff --git a/packages/ember-data/tests/integration/serializers/json-serializer-test.js b/packages/ember-data/tests/integration/serializers/json-serializer-test.js index d9be2147f05..be5c04ee057 100644 --- a/packages/ember-data/tests/integration/serializers/json-serializer-test.js +++ b/packages/ember-data/tests/integration/serializers/json-serializer-test.js @@ -31,7 +31,7 @@ module("integration/serializer/json - JSONSerializer", { test("serializeAttribute", function() { run(function() { - post = env.store.createRecord("post", { title: "Rails is omakase" }); + post = env.store.createRecord('post', { title: "Rails is omakase" }); }); var json = {}; @@ -50,7 +50,7 @@ test("serializeAttribute respects keyForAttribute", function() { })); run(function() { - post = env.store.createRecord("post", { title: "Rails is omakase" }); + post = env.store.createRecord('post', { title: "Rails is omakase" }); }); var json = {}; @@ -61,8 +61,8 @@ test("serializeAttribute respects keyForAttribute", function() { test("serializeBelongsTo", function() { run(function() { - post = env.store.createRecord(Post, { title: "Rails is omakase", id: "1" }); - comment = env.store.createRecord(Comment, { body: "Omakase is delicious", post: post }); + post = env.store.createRecord('post', { title: "Rails is omakase", id: "1" }); + comment = env.store.createRecord('comment', { body: "Omakase is delicious", post: post }); }); var json = {}; @@ -74,7 +74,7 @@ test("serializeBelongsTo", function() { test("serializeBelongsTo with null", function() { run(function() { - comment = env.store.createRecord(Comment, { body: "Omakase is delicious", post: null }); + comment = env.store.createRecord('comment', { body: "Omakase is delicious", post: null }); }); var json = {}; @@ -90,7 +90,7 @@ test("async serializeBelongsTo with null", function() { post: DS.belongsTo('post', { async: true }) }); run(function() { - comment = env.store.createRecord(Comment, { body: "Omakase is delicious", post: null }); + comment = env.store.createRecord('comment', { body: "Omakase is delicious", post: null }); }); var json = {}; @@ -108,8 +108,8 @@ test("serializeBelongsTo respects keyForRelationship", function() { } })); run(function() { - post = env.store.createRecord(Post, { title: "Rails is omakase", id: "1" }); - comment = env.store.createRecord(Comment, { body: "Omakase is delicious", post: post }); + post = env.store.createRecord('post', { title: "Rails is omakase", id: "1" }); + comment = env.store.createRecord('comment', { body: "Omakase is delicious", post: post }); }); var json = {}; @@ -128,8 +128,8 @@ test("serializeHasMany respects keyForRelationship", function() { })); run(function() { - post = env.store.createRecord(Post, { title: "Rails is omakase", id: "1" }); - comment = env.store.createRecord(Comment, { body: "Omakase is delicious", post: post, id: "1" }); + post = env.store.createRecord('post', { title: "Rails is omakase", id: "1" }); + comment = env.store.createRecord('comment', { body: "Omakase is delicious", post: post, id: "1" }); }); var json = {}; @@ -143,7 +143,7 @@ test("serializeHasMany respects keyForRelationship", function() { test("serializeIntoHash", function() { run(function() { - post = env.store.createRecord("post", { title: "Rails is omakase" }); + post = env.store.createRecord('post', { title: "Rails is omakase" }); }); var json = {}; @@ -191,8 +191,8 @@ test("serializePolymorphicType async", function() { })); run(function() { - post = env.store.createRecord(Post, { title: 'Rails is omakase', id: 1 }); - comment = env.store.createRecord(Comment, { body: 'Omakase is delicious', post: post }); + post = env.store.createRecord('post', { title: 'Rails is omakase', id: 1 }); + comment = env.store.createRecord('comment', { body: 'Omakase is delicious', post: post }); }); env.container.lookup('serializer:comment').serializeBelongsTo(comment._createSnapshot(), {}, { key: 'post', options: { async: true, polymorphic: true } }); @@ -250,8 +250,8 @@ test('Serializer should respect the attrs hash when serializing records', functi var parentPost; run(function() { - parentPost = env.store.push("post", { id: 2, title: "Rails is omakase" }); - post = env.store.createRecord("post", { title: "Rails is omakase", parentPost: parentPost }); + parentPost = env.store.push('post', { id: 2, title: "Rails is omakase" }); + post = env.store.createRecord('post', { title: "Rails is omakase", parentPost: parentPost }); }); var payload = env.container.lookup("serializer:post").serialize(post._createSnapshot()); @@ -269,7 +269,7 @@ test('Serializer respects `serialize: false` on the attrs hash', function() { })); run(function() { - post = env.store.createRecord("post", { title: "Rails is omakase" }); + post = env.store.createRecord('post', { title: "Rails is omakase" }); }); var payload = env.container.lookup("serializer:post").serialize(post._createSnapshot()); @@ -287,8 +287,8 @@ test('Serializer respects `serialize: false` on the attrs hash for a `hasMany` p })); run(function() { - post = env.store.createRecord("post", { title: "Rails is omakase" }); - comment = env.store.createRecord(Comment, { body: "Omakase is delicious", post: post }); + post = env.store.createRecord('post', { title: "Rails is omakase" }); + comment = env.store.createRecord('comment', { body: "Omakase is delicious", post: post }); }); var serializer = env.container.lookup("serializer:post"); @@ -307,8 +307,8 @@ test('Serializer respects `serialize: false` on the attrs hash for a `belongsTo` })); run(function() { - post = env.store.createRecord("post", { title: "Rails is omakase" }); - comment = env.store.createRecord(Comment, { body: "Omakase is delicious", post: post }); + post = env.store.createRecord('post', { title: "Rails is omakase" }); + comment = env.store.createRecord('comment', { body: "Omakase is delicious", post: post }); }); var serializer = env.container.lookup("serializer:comment"); @@ -370,7 +370,7 @@ test("Serializer should respect the primaryKey attribute when serializing record })); run(function() { - post = env.store.createRecord("post", { id: "1", title: "Rails is omakase" }); + post = env.store.createRecord('post', { id: "1", title: "Rails is omakase" }); }); var payload = env.container.lookup("serializer:post").serialize(post._createSnapshot(), { includeId: true }); @@ -477,8 +477,8 @@ test('serializeBelongsTo with async polymorphic', function() { })); run(function() { - post = env.store.createRecord(Post, { title: 'Kitties are omakase', id: '1' }); - favorite = env.store.createRecord(Favorite, { post: post, id: '3' }); + post = env.store.createRecord('post', { title: 'Kitties are omakase', id: '1' }); + favorite = env.store.createRecord('favorite', { post: post, id: '3' }); }); env.container.lookup('serializer:favorite').serializeBelongsTo(favorite._createSnapshot(), json, { key: 'post', options: { polymorphic: true, async: true } }); diff --git a/packages/ember-data/tests/integration/serializers/rest-serializer-test.js b/packages/ember-data/tests/integration/serializers/rest-serializer-test.js index 7efe5f88a2a..c2e1f746a79 100644 --- a/packages/ember-data/tests/integration/serializers/rest-serializer-test.js +++ b/packages/ember-data/tests/integration/serializers/rest-serializer-test.js @@ -6,22 +6,22 @@ module("integration/serializer/rest - RESTSerializer", { setup: function() { HomePlanet = DS.Model.extend({ name: DS.attr('string'), - superVillains: DS.hasMany('superVillain') + superVillains: DS.hasMany('super-villain') }); SuperVillain = DS.Model.extend({ firstName: DS.attr('string'), lastName: DS.attr('string'), - homePlanet: DS.belongsTo("homePlanet"), - evilMinions: DS.hasMany("evilMinion") + homePlanet: DS.belongsTo('home-planet'), + evilMinions: DS.hasMany('evil-minion') }); EvilMinion = DS.Model.extend({ - superVillain: DS.belongsTo('superVillain'), + superVillain: DS.belongsTo('super-villain'), name: DS.attr('string') }); YellowMinion = EvilMinion.extend(); DoomsdayDevice = DS.Model.extend({ name: DS.attr('string'), - evilMinion: DS.belongsTo('evilMinion', { polymorphic: true }) + evilMinion: DS.belongsTo('evil-minion', { polymorphic: true }) }); Comment = DS.Model.extend({ body: DS.attr('string'), @@ -36,11 +36,11 @@ module("integration/serializer/rest - RESTSerializer", { doomsdayDevice: DoomsdayDevice, comment: Comment }); - env.store.modelFor('superVillain'); - env.store.modelFor('homePlanet'); - env.store.modelFor('evilMinion'); - env.store.modelFor('yellowMinion'); - env.store.modelFor('doomsdayDevice'); + env.store.modelFor('super-villain'); + env.store.modelFor('home-planet'); + env.store.modelFor('evil-minion'); + env.store.modelFor('yellow-minion'); + env.store.modelFor('doomsday-device'); env.store.modelFor('comment'); }, @@ -80,7 +80,7 @@ test("extractArray with custom modelNameFromPayloadKey", function() { }]); run(function() { - env.store.find("superVillain", 1).then(function(minion) { + env.store.find('super-villain', 1).then(function(minion) { equal(minion.get('firstName'), "Tom"); }); }); @@ -179,18 +179,18 @@ test("pushPayload - single record payload - warning with custom modelNameFromPay warns(function() { run(function() { - env.store.pushPayload("homePlanet", jsonHash); + env.store.pushPayload('home-planet', jsonHash); }); }, /Encountered "home_planet" in payload, but no model was found for model name "garbage"/); // assert non-warned records get pushed into store correctly - var superVillain = env.store.getById("superVillain", "1"); + var superVillain = env.store.getById('super-villain', "1"); equal(get(superVillain, "firstName"), "Stanley"); // Serializers are singletons, so that"s why we use the store which // looks at the container to look it up - env.store.serializerFor("homePlanet").reopen({ + env.store.serializerFor('home-planet').reopen({ modelNameFromPayloadKey: function(root) { // should not warn if a model is found. return Ember.String.camelize(Ember.String.singularize(root)); @@ -204,8 +204,8 @@ test("pushPayload - single record payload - warning with custom modelNameFromPay noWarns(function() { run(function() { - env.store.pushPayload("homePlanet", jsonHash); - homePlanet = env.store.getById("homePlanet", "1"); + env.store.pushPayload('home-planet', jsonHash); + homePlanet = env.store.getById('home-planet', "1"); }); }); @@ -235,17 +235,17 @@ test("pushPayload - multiple record payload (extractArray) - warning with custom warns(function() { run(function() { - env.store.pushPayload("homePlanet", jsonHash); + env.store.pushPayload('home-planet', jsonHash); }); }, /Encountered "home_planets" in payload, but no model was found for model name "garbage"/); // assert non-warned records get pushed into store correctly - var superVillain = env.store.getById("superVillain", "1"); + var superVillain = env.store.getById('super-villain', "1"); equal(get(superVillain, "firstName"), "Stanley"); // Serializers are singletons, so that"s why we use the store which // looks at the container to look it up - env.store.serializerFor("homePlanet").reopen({ + env.store.serializerFor('home-planet').reopen({ modelNameFromPayloadKey: function(root) { // should not warn if a model is found. return Ember.String.camelize(Ember.String.singularize(root)); @@ -259,8 +259,8 @@ test("pushPayload - multiple record payload (extractArray) - warning with custom noWarns(function() { run(function() { - env.store.pushPayload("homePlanet", jsonHash); - homePlanet = env.store.getById("homePlanet", "1"); + env.store.pushPayload('home-planet', jsonHash); + homePlanet = env.store.getById('home-planet', "1"); }); }); @@ -271,8 +271,8 @@ test("pushPayload - multiple record payload (extractArray) - warning with custom test("serialize polymorphicType", function() { var tom, ray; run(function() { - tom = env.store.createRecord(YellowMinion, { name: "Alex", id: "124" }); - ray = env.store.createRecord(DoomsdayDevice, { evilMinion: tom, name: "DeathRay" }); + tom = env.store.createRecord('yellow-minion', { name: "Alex", id: "124" }); + ray = env.store.createRecord('doomsday-device', { evilMinion: tom, name: "DeathRay" }); }); var json = env.restSerializer.serialize(ray._createSnapshot()); @@ -288,8 +288,8 @@ test("serialize polymorphicType with decamelized modelName", function() { YellowMinion.modelName = 'yellow-minion'; var tom, ray; run(function() { - tom = env.store.createRecord(YellowMinion, { name: "Alex", id: "124" }); - ray = env.store.createRecord(DoomsdayDevice, { evilMinion: tom, name: "DeathRay" }); + tom = env.store.createRecord('yellow-minion', { name: "Alex", id: "124" }); + ray = env.store.createRecord('doomsday-device', { evilMinion: tom, name: "DeathRay" }); }); var json = env.restSerializer.serialize(ray._createSnapshot()); @@ -324,7 +324,7 @@ test("normalizePayload is called during extractSingle", function() { test("serialize polymorphic when associated object is null", function() { var ray; run(function() { - ray = env.store.createRecord(DoomsdayDevice, { name: "DeathRay" }); + ray = env.store.createRecord('doomsday-device', { name: "DeathRay" }); }); var json = env.restSerializer.serialize(ray._createSnapshot()); @@ -355,8 +355,8 @@ test("extractArray can load secondary records of the same type without affecting equal(array.length, 1, "The query count is unaffected"); - equal(env.store.recordForId("comment", "2").get("body"), "Child Comment 1", "Secondary records are in the store"); - equal(env.store.recordForId("comment", "3").get("body"), "Child Comment 2", "Secondary records are in the store"); + equal(env.store.recordForId('comment', "2").get("body"), "Child Comment 1", "Secondary records are in the store"); + equal(env.store.recordForId('comment', "3").get("body"), "Child Comment 2", "Secondary records are in the store"); }); test("extractSingle loads secondary records with correct serializer", function() { @@ -511,7 +511,7 @@ test('normalize should allow for different levels of normalization', function() test("serializeIntoHash", function() { run(function() { - league = env.store.createRecord(HomePlanet, { name: "Umber", id: "123" }); + league = env.store.createRecord('home-planet', { name: "Umber", id: "123" }); }); var json = {}; @@ -527,7 +527,7 @@ test("serializeIntoHash", function() { test("serializeIntoHash with decamelized modelName", function() { HomePlanet.modelName = 'home-planet'; run(function() { - league = env.store.createRecord(HomePlanet, { name: "Umber", id: "123" }); + league = env.store.createRecord('home-planet', { name: "Umber", id: "123" }); }); var json = {}; @@ -546,8 +546,8 @@ test('serializeBelongsTo with async polymorphic', function() { var expected = { evilMinion: '1', evilMinionType: 'evilMinion' }; run(function() { - evilMinion = env.store.createRecord('evilMinion', { id: 1, name: 'Tomster' }); - doomsdayDevice = env.store.createRecord('doomsdayDevice', { id: 2, name: 'Yehuda', evilMinion: evilMinion }); + evilMinion = env.store.createRecord('evil-minion', { id: 1, name: 'Tomster' }); + doomsdayDevice = env.store.createRecord('doomsday-device', { id: 2, name: 'Yehuda', evilMinion: evilMinion }); }); env.restSerializer.serializeBelongsTo(doomsdayDevice._createSnapshot(), json, { key: 'evilMinion', options: { polymorphic: true, async: true } }); @@ -557,7 +557,7 @@ test('serializeBelongsTo with async polymorphic', function() { test('serializeIntoHash uses payloadKeyFromModelName to normalize the payload root key', function() { run(function() { - league = env.store.createRecord(HomePlanet, { name: "Umber", id: "123" }); + league = env.store.createRecord('home-planet', { name: "Umber", id: "123" }); }); var json = {}; env.registry.register('serializer:home-planet', DS.RESTSerializer.extend({ diff --git a/packages/ember-data/tests/integration/store-test.js b/packages/ember-data/tests/integration/store-test.js index 854f2939afa..708a25b9c1c 100644 --- a/packages/ember-data/tests/integration/store-test.js +++ b/packages/ember-data/tests/integration/store-test.js @@ -57,7 +57,7 @@ asyncTest("destroying record during find doesn't cause error", function() { find: function(store, type, id, snapshot) { return new Ember.RSVP.Promise(function(resolve, reject) { Ember.run.next(function() { - store.unloadAll(type); + store.unloadAll(type.modelName); reject(); }); }); diff --git a/packages/ember-data/tests/unit/adapter-populated-record-array-test.js b/packages/ember-data/tests/unit/adapter-populated-record-array-test.js index 9de3a89ea5b..74a1a9c6652 100644 --- a/packages/ember-data/tests/unit/adapter-populated-record-array-test.js +++ b/packages/ember-data/tests/unit/adapter-populated-record-array-test.js @@ -9,24 +9,24 @@ var adapter = DS.Adapter.extend({ module("unit/adapter_populated_record_array - DS.AdapterPopulatedRecordArray", { setup: function() { + Person = DS.Model.extend({ + name: DS.attr('string') + }); store = createStore({ - adapter: adapter + adapter: adapter, + person: Person }); - array = [{ id: '1', name: "Scumbag Dale" }, { id: '2', name: "Scumbag Katz" }, { id: '3', name: "Scumbag Bryn" }]; - Person = DS.Model.extend({ - name: DS.attr('string') - }); } }); test("when a record is deleted in an adapter populated record array, it should be removed", function() { var recordArray = store.recordArrayManager - .createAdapterPopulatedRecordArray(Person, null); + .createAdapterPopulatedRecordArray(store.modelFor('person'), null); run(function() { recordArray.load(array); diff --git a/packages/ember-data/tests/unit/debug-test.js b/packages/ember-data/tests/unit/debug-test.js index f4906b99460..95589a6d696 100644 --- a/packages/ember-data/tests/unit/debug-test.js +++ b/packages/ember-data/tests/unit/debug-test.js @@ -16,7 +16,7 @@ test("_debugInfo groups the attributes and relationships correctly", function() var User = DS.Model.extend({ name: DS.attr('string'), isDrugAddict: DS.attr('boolean'), - maritalStatus: DS.belongsTo('maritalStatus'), + maritalStatus: DS.belongsTo('marital-status'), posts: DS.hasMany('post') }); @@ -29,7 +29,7 @@ test("_debugInfo groups the attributes and relationships correctly", function() var record; run(function() { - record = store.createRecord(User); + record = store.createRecord('user'); }); var propertyInfo = record._debugInfo().propertyInfo; diff --git a/packages/ember-data/tests/unit/model-test.js b/packages/ember-data/tests/unit/model-test.js index d6b76fa0ccf..8063ea1dde8 100644 --- a/packages/ember-data/tests/unit/model-test.js +++ b/packages/ember-data/tests/unit/model-test.js @@ -2,16 +2,19 @@ var get = Ember.get; var set = Ember.set; var run = Ember.run; -var Person, store, array; +var Person, store, array, env; module("unit/model - DS.Model", { setup: function() { - store = createStore(); - Person = DS.Model.extend({ name: DS.attr('string'), isDrugAddict: DS.attr('boolean') }); + + env = setupStore({ + person: Person + }); + store = env.store; }, teardown: function() { @@ -26,7 +29,7 @@ module("unit/model - DS.Model", { test("can have a property set on it", function() { var record; run(function() { - record = store.createRecord(Person); + record = store.createRecord('person'); set(record, 'name', 'bar'); }); @@ -37,8 +40,8 @@ test("setting a property on a record that has not changed does not cause it to b expect(2); run(function() { - store.push(Person, { id: 1, name: "Peter", isDrugAddict: true }); - store.find(Person, 1).then(function(person) { + store.push('person', { id: 1, name: "Peter", isDrugAddict: true }); + store.find('person', 1).then(function(person) { equal(person.get('isDirty'), false, "precond - person record should not be dirty"); person.set('name', "Peter"); @@ -53,8 +56,8 @@ test("resetting a property on a record cause it to become clean again", function expect(3); run(function() { - store.push(Person, { id: 1, name: "Peter", isDrugAddict: true }); - store.find(Person, 1).then(function(person) { + store.push('person', { id: 1, name: "Peter", isDrugAddict: true }); + store.find('person', 1).then(function(person) { equal(person.get('isDirty'), false, "precond - person record should not be dirty"); person.set('isDrugAddict', false); equal(person.get('isDirty'), true, "record becomes dirty after setting property to a new value"); @@ -68,8 +71,8 @@ test("a record becomes clean again only if all changed properties are reset", fu expect(5); run(function() { - store.push(Person, { id: 1, name: "Peter", isDrugAddict: true }); - store.find(Person, 1).then(function(person) { + store.push('person', { id: 1, name: "Peter", isDrugAddict: true }); + store.find('person', 1).then(function(person) { equal(person.get('isDirty'), false, "precond - person record should not be dirty"); person.set('isDrugAddict', false); equal(person.get('isDirty'), true, "record becomes dirty after setting one property to a new value"); @@ -87,8 +90,8 @@ test("a record reports its unique id via the `id` property", function() { expect(1); run(function() { - store.push(Person, { id: 1 }); - store.find(Person, 1).then(function(record) { + store.push('person', { id: 1 }); + store.find('person', 1).then(function(record) { equal(get(record, 'id'), 1, "reports id as id by default"); }); }); @@ -98,8 +101,8 @@ test("a record's id is included in its toString representation", function() { expect(1); run(function() { - store.push(Person, { id: 1 }); - store.find(Person, 1).then(function(record) { + store.push('person', { id: 1 }); + store.find('person', 1).then(function(record) { equal(record.toString(), '<(subclass of DS.Model):'+Ember.guidFor(record)+':1>', "reports id in toString"); }); }); @@ -111,10 +114,14 @@ test("trying to set an `id` attribute should raise", function() { name: DS.attr('string') }); + var store = createStore({ + person: Person + }); + expectAssertion(function() { run(function() { - store.push(Person, { id: 1, name: "Scumdale" }); - store.find(Person, 1); + store.push('person', { id: 1, name: "Scumdale" }); + store.find('person', 1); }); }, /You may not set `id`/); }); @@ -128,8 +135,8 @@ test("a collision of a record's id with object function's name", function() { Object.prototype.watch = function() {}; } run(function() { - store.push(Person, { id: 'watch' }); - store.find(Person, 'watch').then(function(record) { + store.push('person', { id: 'watch' }); + store.find('person', 'watch').then(function(record) { equal(get(record, 'id'), 'watch', "record is successfully created and could be found by its id"); }); }); @@ -144,9 +151,9 @@ test("it should use `_reference` and not `reference` to store its reference", fu expect(1); run(function() { - store.push(Person, { id: 1 }); + store.push('person', { id: 1 }); - store.find(Person, 1).then(function(record) { + store.find('person', 1).then(function(record) { equal(record.get('reference'), undefined, "doesn't shadow reference key"); }); }); @@ -155,18 +162,20 @@ test("it should use `_reference` and not `reference` to store its reference", fu test("it should cache attributes", function() { expect(2); - var store = createStore(); - var Post = DS.Model.extend({ updatedAt: DS.attr('string') }); + var store = createStore({ + post: Post + }); + var dateString = "Sat, 31 Dec 2011 00:08:16 GMT"; var date = new Date(dateString); run(function() { - store.push(Post, { id: 1 }); - store.find(Post, 1).then(function(record) { + store.push('post', { id: 1 }); + store.find('post', 1).then(function(record) { run(function() { record.set('updatedAt', date); }); @@ -187,11 +196,16 @@ test("changedAttributes() return correct values", function() { likes: DS.attr('string'), isMascot: DS.attr('boolean') }); + + var store = createStore({ + mascot: Mascot + }); + var mascot; run(function() { - mascot = store.push(Mascot, { id: 1, likes: 'JavaScript', isMascot: true }); + mascot = store.push('mascot', { id: 1, likes: 'JavaScript', isMascot: true }); }); deepEqual({}, mascot.changedAttributes(), 'there are no initial changes'); @@ -212,10 +226,15 @@ test("a DS.Model does not require an attribute type", function() { var Tag = DS.Model.extend({ name: DS.attr() }); + + var store = createStore({ + tag: Tag + }); + var tag; run(function() { - tag = store.createRecord(Tag, { name: "test" }); + tag = store.createRecord('tag', { name: "test" }); }); equal(get(tag, 'name'), "test", "the value is persisted"); @@ -225,10 +244,14 @@ test("a DS.Model can have a defaultValue without an attribute type", function() var Tag = DS.Model.extend({ name: DS.attr({ defaultValue: "unknown" }) }); + + var store = createStore({ + tag: Tag + }); var tag; run(function() { - tag = store.createRecord(Tag); + tag = store.createRecord('tag'); }); equal(get(tag, 'name'), "unknown", "the default value is found"); @@ -241,7 +264,9 @@ test("Calling attr(), belongsTo() or hasMany() throws a warning", function() { name: DS.attr('string') }); - var store = createStore({ person: Person }); + var store = createStore({ + person: Person + }); run(function() { var person = store.createRecord('person', { id: 1, name: 'TomHuda' }); @@ -264,9 +289,9 @@ test("supports pushedData in root.deleted.uncommitted", function() { var record; var hash = { id: 1 }; run(function() { - record = store.push(Person, hash); + record = store.push('person', hash); record.deleteRecord(); - store.push(Person, hash); + store.push('person', hash); equal(get(record, 'currentState.stateName'), 'root.deleted.uncommitted', 'record accepts pushedData is in root.deleted.uncommitted state'); }); @@ -277,9 +302,12 @@ module("unit/model - DS.Model updating", { setup: function() { array = [{ id: 1, name: "Scumbag Dale" }, { id: 2, name: "Scumbag Katz" }, { id: 3, name: "Scumbag Bryn" }]; Person = DS.Model.extend({ name: DS.attr('string') }); - store = createStore(); + env = setupStore({ + person: Person + }); + store = env.store; run(function() { - store.pushMany(Person, array); + store.pushMany('person', array); }); }, teardown: function() { @@ -296,7 +324,7 @@ test("a DS.Model can update its attributes", function() { expect(1); run(function() { - store.find(Person, 2).then(function(person) { + store.find('person', 2).then(function(person) { set(person, 'name', "Brohuda Katz"); equal(get(person, 'name'), "Brohuda Katz", "setting took hold"); }); @@ -309,8 +337,12 @@ test("a DS.Model can have a defaultValue", function() { }); var tag; + var store = createStore({ + tag: Tag + }); + run(function() { - tag = store.createRecord(Tag); + tag = store.createRecord('tag'); }); equal(get(tag, 'name'), "unknown", "the default value is found"); @@ -334,8 +366,12 @@ test("a DS.model can define 'setUnknownProperty'", function() { } }); + var store = createStore({ + tag: Tag + }); + run(function() { - tag = store.createRecord(Tag, { name: "old" }); + tag = store.createRecord('tag', { name: "old" }); set(tag, "title", "new"); }); @@ -352,8 +388,12 @@ test("a defaultValue for an attribute can be a function", function() { }); var tag; + var store = createStore({ + tag: Tag + }); + run(function() { - tag = store.createRecord(Tag); + tag = store.createRecord('tag'); }); equal(get(tag, 'createdAt'), "le default value", "the defaultValue function is evaluated"); }); @@ -370,10 +410,14 @@ test("a defaultValue function gets the record, options, and key", function() { } }) }); + + var store = createStore({ + tag: Tag + }); var tag; run(function() { - tag = store.createRecord(Tag); + tag = store.createRecord('tag'); }); get(tag, 'createdAt'); @@ -383,10 +427,15 @@ test("setting a property to undefined on a newly created record should not impac var Tag = DS.Model.extend({ name: DS.attr('string') }); + + var store = createStore({ + tag: Tag + }); + var tag; run(function() { - tag = store.createRecord(Tag); + tag = store.createRecord('tag'); set(tag, 'name', 'testing'); set(tag, 'name', undefined); }); @@ -394,7 +443,7 @@ test("setting a property to undefined on a newly created record should not impac equal(get(tag, 'currentState.stateName'), "root.loaded.created.uncommitted"); run(function() { - tag = store.createRecord(Tag, { name: undefined }); + tag = store.createRecord('tag', { name: undefined }); }); equal(get(tag, 'currentState.stateName'), "root.loaded.created.uncommitted"); @@ -406,7 +455,7 @@ test("setting a property back to its original value removes the property from th expect(3); run(function() { - store.find(Person, 1).then(function(person) { + store.find('person', 1).then(function(person) { equal(person._attributes.name, undefined, "the `_attributes` hash is clean"); set(person, 'name', "Niceguy Dale"); @@ -434,7 +483,7 @@ module("unit/model - with a simple Person model", { person: Person }); run(function() { - store.pushMany(Person, array); + store.pushMany('person', array); }); }, teardown: function() { @@ -448,9 +497,9 @@ module("unit/model - with a simple Person model", { }); test("can ask if record with a given id is loaded", function() { - equal(store.recordIsLoaded(Person, 1), true, 'should have person with id 1'); equal(store.recordIsLoaded('person', 1), true, 'should have person with id 1'); - equal(store.recordIsLoaded(Person, 4), false, 'should not have person with id 4'); + equal(store.recordIsLoaded('person', 1), true, 'should have person with id 1'); + equal(store.recordIsLoaded('person', 4), false, 'should not have person with id 4'); equal(store.recordIsLoaded('person', 4), false, 'should not have person with id 4'); }); @@ -460,7 +509,7 @@ test("a listener can be added to a record", function() { var record; run(function() { - record = store.createRecord(Person); + record = store.createRecord('person'); }); record.on('event!', F); @@ -483,7 +532,7 @@ test("when an event is triggered on a record the method with the same name is in var record; run(function() { - record = store.createRecord(Person); + record = store.createRecord('person'); }); record.eventNamedMethod = F; @@ -503,7 +552,7 @@ test("when a method is invoked from an event with the same name the arguments ar var record; run(function() { - record = store.createRecord(Person); + record = store.createRecord('person'); }); record.eventThatTriggersMethod = F; @@ -536,8 +585,8 @@ var converts = function(type, provided, expected) { }); run(function() { - testStore.push(Model, serializer.normalize(Model, { id: 1, name: provided })); - testStore.push(Model, serializer.normalize(Model, { id: 2 })); + testStore.push('model', serializer.normalize(Model, { id: 1, name: provided })); + testStore.push('model', serializer.normalize(Model, { id: 2 })); testStore.find('model', 1).then(function(record) { deepEqual(get(record, 'name'), expected, type + " coerces " + provided + " to " + expected); }); @@ -569,7 +618,7 @@ var convertsFromServer = function(type, provided, expected) { }); run(function() { - testStore.push(Model, serializer.normalize(Model, { id: "1", name: provided })); + testStore.push('model', serializer.normalize(Model, { id: "1", name: provided })); testStore.find('model', 1).then(function(record) { deepEqual(get(record, 'name'), expected, type + " coerces " + provided + " to " + expected); }); @@ -584,7 +633,7 @@ var convertsWhenSet = function(type, provided, expected) { var testStore = createStore({ model: Model }); run(function() { - testStore.push(Model, { id: 2 }); + testStore.push('model', { id: 2 }); testStore.find('model', 2).then(function(record) { set(record, 'name', provided); deepEqual(record.serialize().name, expected, type + " saves " + provided + " as " + expected); @@ -638,15 +687,18 @@ test("a DS.Model can describe Date attributes", function() { var dateString = "2011-12-31T00:08:16.000Z"; var date = new Date(Ember.Date.parse(dateString)); - var store = createStore(); var Person = DS.Model.extend({ updatedAt: DS.attr('date') }); + var store = createStore({ + person: Person + }); + run(function() { - store.push(Person, { id: 1 }); - store.find(Person, 1).then(function(record) { + store.push('person', { id: 1 }); + store.find('person', 1).then(function(record) { run(function() { record.set('updatedAt', date); }); @@ -658,13 +710,15 @@ test("a DS.Model can describe Date attributes", function() { }); test("don't allow setting", function() { - var store = createStore(); - var Person = DS.Model.extend(); var record; + var store = createStore({ + person: Person + }); + run(function() { - record = store.createRecord(Person); + record = store.createRecord('person'); }); raises(function() { @@ -682,11 +736,12 @@ test("ensure model exits loading state, materializes data and fulfills promise o find: function(store, type, id, snapshot) { return Ember.RSVP.resolve({ id: 1, name: "John" }); } - }) + }), + person: Person }); run(function() { - store.find(Person, 1).then(function(person) { + store.find('person', 1).then(function(person) { equal(get(person, 'currentState.stateName'), 'root.loaded.saved', 'model is in loaded state'); equal(get(person, 'isLoaded'), true, 'model is loaded'); }); diff --git a/packages/ember-data/tests/unit/model/lifecycle-callbacks-test.js b/packages/ember-data/tests/unit/model/lifecycle-callbacks-test.js index 219f5998716..45cbd32c730 100644 --- a/packages/ember-data/tests/unit/model/lifecycle-callbacks-test.js +++ b/packages/ember-data/tests/unit/model/lifecycle-callbacks-test.js @@ -20,11 +20,12 @@ test("a record receives a didLoad callback when it has finished loading", functi }); var store = createStore({ - adapter: adapter + adapter: adapter, + person: Person }); run(function() { - store.find(Person, 1).then(function(person) { + store.find('person', 1).then(function(person) { equal(person.get('id'), "1", "The person's ID is available"); equal(person.get('name'), "Foo", "The person's properties are available"); }); @@ -60,12 +61,13 @@ test("a record receives a didUpdate callback when it has finished updating", fun }); var store = createStore({ - adapter: adapter + adapter: adapter, + person: Person }); var asyncPerson; run(function() { - asyncPerson = store.find(Person, 1); + asyncPerson = store.find('person', 1); }); equal(callCount, 0, "precond - didUpdate callback was not called yet"); @@ -103,14 +105,15 @@ test("a record receives a didCreate callback when it has finished updating", fun }); var store = createStore({ - adapter: adapter + adapter: adapter, + person: Person }); equal(callCount, 0, "precond - didCreate callback was not called yet"); var person; run(function() { - person = store.createRecord(Person, { id: 69, name: "Newt Gingrich" }); + person = store.createRecord('person', { id: 69, name: "Newt Gingrich" }); }); @@ -151,12 +154,13 @@ test("a record receives a didDelete callback when it has finished deleting", fun }); var store = createStore({ - adapter: adapter + adapter: adapter, + person: Person }); var asyncPerson; run(function() { - asyncPerson = store.find(Person, 1); + asyncPerson = store.find('person', 1); }); equal(callCount, 0, "precond - didDelete callback was not called yet"); @@ -190,12 +194,13 @@ test("an uncommited record also receives a didDelete callback when it is deleted }); var store = createStore({ - adapter: DS.Adapter.extend() + adapter: DS.Adapter.extend(), + person: Person }); var person; run(function() { - person = store.createRecord(Person, { name: 'Tomster' }); + person = store.createRecord('person', { name: 'Tomster' }); }); equal(callCount, 0, "precond - didDelete callback was not called yet"); @@ -237,12 +242,13 @@ test("a record receives a becameInvalid callback when it became invalid", functi }); var store = createStore({ - adapter: adapter + adapter: adapter, + person: Person }); var asyncPerson; run(function() { - asyncPerson = store.find(Person, 1); + asyncPerson = store.find('person', 1); }); equal(callCount, 0, "precond - becameInvalid callback was not called yet"); @@ -261,15 +267,18 @@ test("a record receives a becameInvalid callback when it became invalid", functi }); test("an ID of 0 is allowed", function() { - var store = createStore(); var Person = DS.Model.extend({ name: DS.attr('string') }); + var store = createStore({ + person: Person + }); + run(function() { - store.push(Person, { id: 0, name: "Tom Dale" }); + store.push('person', { id: 0, name: "Tom Dale" }); }); - equal(store.all(Person).objectAt(0).get('name'), "Tom Dale", "found record with id 0"); + equal(store.all('person').objectAt(0).get('name'), "Tom Dale", "found record with id 0"); }); diff --git a/packages/ember-data/tests/unit/model/merge-test.js b/packages/ember-data/tests/unit/model/merge-test.js index bd84896158b..353099942a3 100644 --- a/packages/ember-data/tests/unit/model/merge-test.js +++ b/packages/ember-data/tests/unit/model/merge-test.js @@ -19,10 +19,13 @@ test("When a record is in flight, changes can be made", function() { } }); var person; - var store = createStore({ adapter: adapter }); + var store = createStore({ + adapter: adapter, + person: Person + }); run(function() { - person = store.createRecord(Person, { name: "Tom Dale" }); + person = store.createRecord('person', { name: "Tom Dale" }); }); // Make sure saving isn't resolved synchronously @@ -52,11 +55,14 @@ test("When a record is in flight, pushes are applied underneath the in flight ch } }); - var store = createStore({ adapter: adapter }); + var store = createStore({ + adapter: adapter, + person: Person + }); var person; run(function() { - person = store.push(Person, { id: 1, name: "Tom" }); + person = store.push('person', { id: 1, name: "Tom" }); person.set('name', "Thomas Dale"); }); @@ -67,7 +73,7 @@ test("When a record is in flight, pushes are applied underneath the in flight ch person.set('name', "Tomasz Dale"); - store.push(Person, { id: 1, name: "Tommy Dale", city: "PDX" }); + store.push('person', { id: 1, name: "Tommy Dale", city: "PDX" }); equal(person.get('name'), "Tomasz Dale", "the local changes applied on top"); equal(person.get('city'), "PDX", "the pushed change is available"); @@ -81,11 +87,14 @@ test("When a record is in flight, pushes are applied underneath the in flight ch }); test("When a record is dirty, pushes are overridden by local changes", function() { - var store = createStore({ adapter: DS.Adapter }); + var store = createStore({ + adapter: DS.Adapter, + person: Person + }); var person; run(function() { - person = store.push(Person, { id: 1, name: "Tom Dale", city: "San Francisco" }); + person = store.push('person', { id: 1, name: "Tom Dale", city: "San Francisco" }); person.set('name', "Tomasz Dale"); }); @@ -94,7 +103,7 @@ test("When a record is dirty, pushes are overridden by local changes", function( equal(person.get('city'), "San Francisco", "the original data applies"); run(function() { - store.push(Person, { id: 1, name: "Thomas Dale", city: "Portland" }); + store.push('person', { id: 1, name: "Thomas Dale", city: "Portland" }); }); equal(person.get('isDirty'), true, "the local changes are reapplied"); @@ -111,11 +120,14 @@ test("A record with no changes can still be saved", function() { } }); - var store = createStore({ adapter: adapter }); + var store = createStore({ + adapter: adapter, + person: Person + }); var person; run(function() { - person = store.push(Person, { id: 1, name: "Tom Dale" }); + person = store.push('person', { id: 1, name: "Tom Dale" }); }); run(function() { @@ -134,11 +146,14 @@ test("A dirty record can be reloaded", function() { } }); - var store = createStore({ adapter: adapter }); + var store = createStore({ + adapter: adapter, + person: Person + }); var person; run(function() { - person = store.push(Person, { id: 1, name: "Tom Dale" }); + person = store.push('person', { id: 1, name: "Tom Dale" }); person.set('name', "Tomasz Dale"); }); diff --git a/packages/ember-data/tests/unit/model/relationships/belongs-to-test.js b/packages/ember-data/tests/unit/model/relationships/belongs-to-test.js index 0b788e23ff0..65e5cdc2c44 100644 --- a/packages/ember-data/tests/unit/model/relationships/belongs-to-test.js +++ b/packages/ember-data/tests/unit/model/relationships/belongs-to-test.js @@ -134,7 +134,7 @@ test("calling createRecord and passing in an undefined value for a relationship }); run(function() { - store.find(Person, 1).then(async(function(person) { + store.find('person', 1).then(async(function(person) { strictEqual(person.get('tag'), null, "undefined values should return null relationships"); })); }); @@ -249,7 +249,7 @@ test("belongsTo supports relationships to models with id 0", function() { equal(get(person, 'tag.name'), "friendly", "the tag should have name"); strictEqual(get(person, 'tag'), get(person, 'tag'), "the returned object is always the same"); - asyncEqual(get(person, 'tag'), store.find(Tag, 0), "relationship object is the same as object retrieved directly"); + asyncEqual(get(person, 'tag'), store.find('tag', 0), "relationship object is the same as object retrieved directly"); })); }); }); diff --git a/packages/ember-data/tests/unit/model/relationships/has-many-test.js b/packages/ember-data/tests/unit/model/relationships/has-many-test.js index dad161ea1b3..a637bcab2f9 100644 --- a/packages/ember-data/tests/unit/model/relationships/has-many-test.js +++ b/packages/ember-data/tests/unit/model/relationships/has-many-test.js @@ -64,7 +64,7 @@ test("hasMany handles pre-loaded relationships", function() { equal(get(get(person, 'tags'), 'length'), 2, "the length is updated after new data is loaded"); strictEqual(get(person, 'tags').objectAt(0), get(person, 'tags').objectAt(0), "the returned object is always the same"); - asyncEqual(get(person, 'tags').objectAt(0), store.find(Tag, 5), "relationship objects are the same as objects retrieved directly"); + asyncEqual(get(person, 'tags').objectAt(0), store.find('tag', 5), "relationship objects are the same as objects retrieved directly"); run(function() { store.push('person', { id: 3, name: "KSelden" }); @@ -86,7 +86,7 @@ test("hasMany handles pre-loaded relationships", function() { equal(get(pets.objectAt(0), 'name'), "fluffy", "the first pet should be correct"); run(function() { - store.push(Person, { id: 4, name: "Cyvid Hamluck", pets: [4, 12] }); + store.push('person', { id: 4, name: "Cyvid Hamluck", pets: [4, 12] }); }); equal(pets, get(cyvid, 'pets'), "a relationship returns the same object every time"); @@ -152,13 +152,13 @@ test("hasMany lazily loads async relationships", function() { equal(get(records.tags.objectAt(0), 'name'), "oohlala", "the first tag should be a Tag"); strictEqual(records.tags.objectAt(0), records.tags.objectAt(0), "the returned object is always the same"); - asyncEqual(records.tags.objectAt(0), store.find(Tag, 12), "relationship objects are the same as objects retrieved directly"); + asyncEqual(records.tags.objectAt(0), store.find('tag', 12), "relationship objects are the same as objects retrieved directly"); return get(wycats, 'tags'); }).then(function(tags) { var newTag; run(function() { - newTag = store.createRecord(Tag); + newTag = store.createRecord('tag'); tags.pushObject(newTag); }); }); @@ -335,12 +335,12 @@ test("it is possible to add a new item to a relationship", function() { }); run(function() { - store.find(Person, 1).then(function(person) { + store.find('person', 1).then(function(person) { var tag = get(person, 'tags').objectAt(0); equal(get(tag, 'name'), "ember", "precond - relationships work"); - tag = store.createRecord(Tag, { name: "js" }); + tag = store.createRecord('tag', { name: "js" }); get(person, 'tags').pushObject(tag); equal(get(person, 'tags').objectAt(1), tag, "newly added relationship works"); diff --git a/packages/ember-data/tests/unit/record-array-test.js b/packages/ember-data/tests/unit/record-array-test.js index 8f59201e64d..2992d8b98a9 100644 --- a/packages/ember-data/tests/unit/record-array-test.js +++ b/packages/ember-data/tests/unit/record-array-test.js @@ -16,13 +16,15 @@ module("unit/record_array - DS.RecordArray", { test("a record array is backed by records", function() { expect(3); - var store = createStore(); + var store = createStore({ + person: Person + }); run(function() { - store.pushMany(Person, array); + store.pushMany('person', array); }); run(function() { - store.findByIds(Person, [1,2,3]).then(function(records) { + store.findByIds('person', [1,2,3]).then(function(records) { for (var i=0, l=get(array, 'length'); i Date: Sat, 16 May 2015 19:07:52 -0500 Subject: [PATCH 2/4] remove instances of DS.Store from factories When using multiple stores with Ember Data, we want to avoid storing the state on the factory itself. Because multiple stores currently share the same factory, this means that the first store to look up the factory will store itself as the `store` on that factory. That means that any stores thereafter will receive factories whose `store` property references the first store, not their own. --- .../lib/serializers/embedded-records-mixin.js | 2 +- .../lib/serializers/json-serializer.js | 2 +- .../lib/system/relationship-meta.js | 20 ++++-------- .../lib/system/relationships/ext.js | 31 ++++++++++--------- .../system/relationships/state/belongs-to.js | 12 +++---- .../lib/system/relationships/state/create.js | 2 +- .../system/relationships/state/has-many.js | 14 ++++----- packages/ember-data/lib/system/store.js | 1 - .../ember-data/lib/system/store/finders.js | 10 +++--- .../tests/integration/inverse-test.js | 18 ++++++----- .../relationships/belongs-to-test.js | 13 ++++++-- .../relationships/has-many-test.js | 8 ++--- .../tests/unit/model/relationships-test.js | 4 +-- .../unit/model/relationships/has-many-test.js | 8 ++--- .../tests/unit/store/create-record-test.js | 4 +-- 15 files changed, 77 insertions(+), 72 deletions(-) diff --git a/packages/ember-data/lib/serializers/embedded-records-mixin.js b/packages/ember-data/lib/serializers/embedded-records-mixin.js index adc0ddcbc99..0e4a105755b 100644 --- a/packages/ember-data/lib/serializers/embedded-records-mixin.js +++ b/packages/ember-data/lib/serializers/embedded-records-mixin.js @@ -391,7 +391,7 @@ function extractEmbeddedRecords(serializer, store, typeClass, partial) { typeClass.eachRelationship(function(key, relationship) { if (serializer.hasDeserializeRecordsOption(key)) { - var embeddedTypeClass = store.modelFor(relationship.type.modelName); + var embeddedTypeClass = store.modelFor(relationship.type); if (relationship.kind === "hasMany") { if (relationship.options.polymorphic) { extractEmbeddedHasManyPolymorphic(store, key, partial); diff --git a/packages/ember-data/lib/serializers/json-serializer.js b/packages/ember-data/lib/serializers/json-serializer.js index e81133c090b..533b6822c6d 100644 --- a/packages/ember-data/lib/serializers/json-serializer.js +++ b/packages/ember-data/lib/serializers/json-serializer.js @@ -651,7 +651,7 @@ export default Serializer.extend({ payloadKey = this.keyForRelationship(key, "hasMany", "serialize"); } - var relationshipType = snapshot.type.determineRelationshipType(relationship); + var relationshipType = snapshot.type.determineRelationshipType(relationship, this.store); if (relationshipType === 'manyToNone' || relationshipType === 'manyToMany') { json[payloadKey] = snapshot.hasMany(key, { ids: true }); diff --git a/packages/ember-data/lib/system/relationship-meta.js b/packages/ember-data/lib/system/relationship-meta.js index 42244245a85..3ec11c098f8 100644 --- a/packages/ember-data/lib/system/relationship-meta.js +++ b/packages/ember-data/lib/system/relationship-meta.js @@ -1,26 +1,18 @@ import {singularize} from 'ember-inflector/lib/system/string'; +import normalizeModelName from 'ember-data/system/normalize-model-name'; -export function typeForRelationshipMeta(store, meta) { - var modelName, typeClass; +export function typeForRelationshipMeta(meta) { + var modelName; modelName = meta.type || meta.key; - if (typeof modelName === 'string') { - if (meta.kind === 'hasMany') { - modelName = singularize(modelName); - } - typeClass = store.modelFor(modelName); - } else { - typeClass = meta.type; - } - - return typeClass; + return singularize(normalizeModelName(modelName)); } -export function relationshipFromMeta(store, meta) { +export function relationshipFromMeta(meta) { return { key: meta.key, kind: meta.kind, - type: typeForRelationshipMeta(store, meta), + type: typeForRelationshipMeta(meta), options: meta.options, parentType: meta.parentType, isRelationship: true diff --git a/packages/ember-data/lib/system/relationships/ext.js b/packages/ember-data/lib/system/relationships/ext.js index a610e220203..7c89de481de 100644 --- a/packages/ember-data/lib/system/relationships/ext.js +++ b/packages/ember-data/lib/system/relationships/ext.js @@ -26,7 +26,7 @@ var relationshipsDescriptor = Ember.computed(function() { // it to the map. if (meta.isRelationship) { meta.key = name; - var relationshipsForType = map.get(typeForRelationshipMeta(this.store, meta)); + var relationshipsForType = map.get(typeForRelationshipMeta(meta)); relationshipsForType.push({ name: name, @@ -52,7 +52,7 @@ var relatedTypesDescriptor = Ember.computed(function() { this.eachComputedProperty(function(name, meta) { if (meta.isRelationship) { meta.key = name; - modelName = typeForRelationshipMeta(this.store, meta); + modelName = typeForRelationshipMeta(meta); Ember.assert("You specified a hasMany (" + meta.type + ") on " + meta.parentType + " but " + meta.type + " was not found.", modelName); @@ -76,8 +76,8 @@ var relationshipsByNameDescriptor = Ember.computed(function() { this.eachComputedProperty(function(name, meta) { if (meta.isRelationship) { meta.key = name; - var relationship = relationshipFromMeta(this.store, meta); - relationship.type = typeForRelationshipMeta(this.store, meta); + var relationship = relationshipFromMeta(meta); + relationship.type = typeForRelationshipMeta(meta); map.set(name, relationship); } }); @@ -175,11 +175,12 @@ Model.reopenClass({ @method typeForRelationship @static @param {String} name the name of the relationship + @param {store} store an instance of DS.Store @return {subclass of DS.Model} the type of the relationship, or undefined */ - typeForRelationship: function(name) { + typeForRelationship: function(name, store) { var relationship = get(this, 'relationshipsByName').get(name); - return relationship && relationship.type; + return relationship && store.modelFor(relationship.type); }, inverseMap: Ember.computed(function() { @@ -209,21 +210,21 @@ Model.reopenClass({ @param {String} name the name of the relationship @return {Object} the inverse relationship, or null */ - inverseFor: function(name) { + inverseFor: function(name, store) { var inverseMap = get(this, 'inverseMap'); if (inverseMap[name]) { return inverseMap[name]; } else { - var inverse = this._findInverseFor(name); + var inverse = this._findInverseFor(name, store); inverseMap[name] = inverse; return inverse; } }, //Calculate the inverse, ignoring the cache - _findInverseFor: function(name) { + _findInverseFor: function(name, store) { - var inverseType = this.typeForRelationship(name); + var inverseType = this.typeForRelationship(name, store); if (!inverseType) { return null; } @@ -277,9 +278,9 @@ Model.reopenClass({ var possibleRelationships = relationshipsSoFar || []; var relationshipMap = get(inverseType, 'relationships'); - if (!relationshipMap) { return; } + if (!relationshipMap) { return possibleRelationships; } - var relationships = relationshipMap.get(type); + var relationships = relationshipMap.get(type.modelName); relationships = filter.call(relationships, function(relationship) { var optionsForRelationship = inverseType.metaForProperty(relationship.name).options; @@ -535,10 +536,10 @@ Model.reopenClass({ }); }, - determineRelationshipType: function(knownSide) { + determineRelationshipType: function(knownSide, store) { var knownKey = knownSide.key; var knownKind = knownSide.kind; - var inverse = this.inverseFor(knownKey); + var inverse = this.inverseFor(knownKey, store); var key, otherKind; if (!inverse) { @@ -617,7 +618,7 @@ Model.reopen({ }, inverseFor: function(key) { - return this.constructor.inverseFor(key); + return this.constructor.inverseFor(key, this.store); } }); diff --git a/packages/ember-data/lib/system/relationships/state/belongs-to.js b/packages/ember-data/lib/system/relationships/state/belongs-to.js index a515aa6aa57..b658ca20cdf 100644 --- a/packages/ember-data/lib/system/relationships/state/belongs-to.js +++ b/packages/ember-data/lib/system/relationships/state/belongs-to.js @@ -61,15 +61,15 @@ BelongsToRelationship.prototype.flushCanonical = function() { BelongsToRelationship.prototype._super$addRecord = Relationship.prototype.addRecord; BelongsToRelationship.prototype.addRecord = function(newRecord) { if (this.members.has(newRecord)) { return;} - var type = this.relationshipMeta.type; - Ember.assert("You cannot add a '" + newRecord.constructor.modelName + "' record to the '" + this.record.constructor.modelName + "." + this.key +"'. " + "You can only add a '" + type.modelName + "' record to this relationship.", (function () { - if (type.__isMixin) { - return type.__mixin.detect(newRecord); + var typeClass = this.store.modelFor(this.relationshipMeta.type); + Ember.assert("You cannot add a '" + newRecord.constructor.modelName + "' record to the '" + this.record.constructor.modelName + "." + this.key +"'. " + "You can only add a '" + typeClass.modelName + "' record to this relationship.", (function () { + if (typeClass.__isMixin) { + return typeClass.__mixin.detect(newRecord); } if (Ember.MODEL_FACTORY_INJECTIONS) { - type = type.superclass; + typeClass = typeClass.superclass; } - return newRecord instanceof type; + return newRecord instanceof typeClass; })()); if (this.inverseRecord) { diff --git a/packages/ember-data/lib/system/relationships/state/create.js b/packages/ember-data/lib/system/relationships/state/create.js index 9d4bd93ae4c..778d5d44087 100644 --- a/packages/ember-data/lib/system/relationships/state/create.js +++ b/packages/ember-data/lib/system/relationships/state/create.js @@ -3,7 +3,7 @@ import BelongsToRelationship from "ember-data/system/relationships/state/belongs var createRelationshipFor = function(record, relationshipMeta, store) { var inverseKey; - var inverse = record.constructor.inverseFor(relationshipMeta.key); + var inverse = record.constructor.inverseFor(relationshipMeta.key, store); if (inverse) { inverseKey = inverse.name; diff --git a/packages/ember-data/lib/system/relationships/state/has-many.js b/packages/ember-data/lib/system/relationships/state/has-many.js index 0c774db41ec..775f47a08a7 100644 --- a/packages/ember-data/lib/system/relationships/state/has-many.js +++ b/packages/ember-data/lib/system/relationships/state/has-many.js @@ -11,7 +11,7 @@ var ManyRelationship = function(store, record, inverseKey, relationshipMeta) { canonicalState: this.canonicalState, store: this.store, relationship: this, - type: this.belongsToType, + type: this.store.modelFor(this.belongsToType), record: record }); this.isPolymorphic = relationshipMeta.options.polymorphic; @@ -84,15 +84,15 @@ ManyRelationship.prototype.removeRecordFromOwn = function(record, idx) { }; ManyRelationship.prototype.notifyRecordRelationshipAdded = function(record, idx) { - var type = this.relationshipMeta.type; - Ember.assert("You cannot add '" + record.constructor.modelName + "' records to the " + this.record.constructor.modelName + "." + this.key + " relationship (only '" + this.belongsToType.modelName + "' allowed)", (function () { - if (type.__isMixin) { - return type.__mixin.detect(record); + var typeClass = this.store.modelFor(this.relationshipMeta.type); + Ember.assert("You cannot add '" + record.constructor.modelName + "' records to the " + this.record.constructor.modelName + "." + this.key + " relationship (only '" + typeClass.modelName + "' allowed)", (function () { + if (typeClass.__isMixin) { + return typeClass.__mixin.detect(record); } if (Ember.MODEL_FACTORY_INJECTIONS) { - type = type.superclass; + typeClass = typeClass.superclass; } - return record instanceof type; + return record instanceof typeClass; })()); this.record.notifyHasManyAdded(this.key, record, idx); diff --git a/packages/ember-data/lib/system/store.js b/packages/ember-data/lib/system/store.js index bc84050d148..4777fc8d4f6 100644 --- a/packages/ember-data/lib/system/store.js +++ b/packages/ember-data/lib/system/store.js @@ -1498,7 +1498,6 @@ Store = Service.extend({ }); } - factory.store = this; return factory; }, diff --git a/packages/ember-data/lib/system/store/finders.js b/packages/ember-data/lib/system/store/finders.js index c633f5f3252..0296ef02fc1 100644 --- a/packages/ember-data/lib/system/store/finders.js +++ b/packages/ember-data/lib/system/store/finders.js @@ -67,7 +67,8 @@ export function _findMany(adapter, store, typeClass, ids, records) { export function _findHasMany(adapter, store, record, link, relationship) { var snapshot = record._createSnapshot(); - var modelName = relationship.type.modelName; + var modelName = relationship.type; + var typeClass = store.modelFor(modelName); var promise = adapter.findHasMany(store, snapshot, link, relationship); var serializer = serializerForAdapter(store, adapter, modelName); var label = "DS: Handle Adapter#findHasMany of " + record + " : " + relationship.type; @@ -78,7 +79,7 @@ export function _findHasMany(adapter, store, record, link, relationship) { return promise.then(function(adapterPayload) { return store._adapterRun(function() { - var payload = serializer.extract(store, relationship.type, adapterPayload, null, 'findHasMany'); + var payload = serializer.extract(store, typeClass, adapterPayload, null, 'findHasMany'); Ember.assert("The response from a findHasMany must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array'); @@ -89,7 +90,8 @@ export function _findHasMany(adapter, store, record, link, relationship) { } export function _findBelongsTo(adapter, store, record, link, relationship) { - var modelName = relationship.type.modelName; + var modelName = relationship.type; + var typeClass = store.modelFor(modelName); var snapshot = record._createSnapshot(); var promise = adapter.findBelongsTo(store, snapshot, link, relationship); var serializer = serializerForAdapter(store, adapter, modelName); @@ -101,7 +103,7 @@ export function _findBelongsTo(adapter, store, record, link, relationship) { return promise.then(function(adapterPayload) { return store._adapterRun(function() { - var payload = serializer.extract(store, relationship.type, adapterPayload, null, 'findBelongsTo'); + var payload = serializer.extract(store, typeClass, adapterPayload, null, 'findBelongsTo'); if (!payload) { return null; diff --git a/packages/ember-data/tests/integration/inverse-test.js b/packages/ember-data/tests/integration/inverse-test.js index e55f15ec4b9..d0861e54ae4 100644 --- a/packages/ember-data/tests/integration/inverse-test.js +++ b/packages/ember-data/tests/integration/inverse-test.js @@ -38,6 +38,10 @@ module('integration/inverse_test - inverseFor', { }); store = env.store; + + Job = store.modelFor('job'); + User = store.modelFor('user'); + ReflexiveModel = store.modelFor('reflexive-model'); }, teardown: function() { @@ -49,7 +53,7 @@ test("Finds the inverse when there is only one possible available", function () //Maybe store is evaluated lazily, so we need this :( run(store, 'push', 'user', { id: 1 }); - deepEqual(Job.inverseFor('user'), { + deepEqual(Job.inverseFor('user', store), { type: User, name: 'job', kind: 'belongsTo' @@ -72,13 +76,13 @@ test("Finds the inverse when only one side has defined it manually", function () job = store.push('user', { id: 1 }); }); - deepEqual(Job.inverseFor('owner'), { + deepEqual(Job.inverseFor('owner', store), { type: User, //the model's type name: 'previousJob', //the models relationship key kind: 'belongsTo' }, 'Gets correct type, name and kind'); - deepEqual(User.inverseFor('previousJob'), { + deepEqual(User.inverseFor('previousJob', store), { type: Job, //the model's type name: 'owner', //the models relationship key kind: 'belongsTo' @@ -99,7 +103,7 @@ test("Returns null if inverse relationship it is manually set with a different r user = store.push('user', { id: 1 }); }); - equal(User.inverseFor('job'), null, 'There is no inverse'); + equal(User.inverseFor('job', store), null, 'There is no inverse'); }); test("Errors out if you define 2 inverses to the same model", function () { @@ -117,7 +121,7 @@ test("Errors out if you define 2 inverses to the same model", function () { run(function() { store.push('user', { id: 1 }); }); - User.inverseFor('job'); + User.inverseFor('job', store); }, "You defined the 'job' relationship on user, but you defined the inverse relationships of type job multiple times. Look at http://emberjs.com/guides/models/defining-models/#toc_explicit-inverses for how to explicitly specify inverses"); }); @@ -129,12 +133,12 @@ test("Caches findInverseFor return value", function () { store.push('user', { id: 1 }); }); - var inverseForUser = Job.inverseFor('user'); + var inverseForUser = Job.inverseFor('user', store); Job.findInverseFor = function() { ok(false, 'Find is not called anymore'); }; - equal(inverseForUser, Job.inverseFor('user'), 'Inverse cached succesfully'); + equal(inverseForUser, Job.inverseFor('user', store), 'Inverse cached succesfully'); }); test("Errors out if you do not define an inverse for a reflexive relationship", function () { diff --git a/packages/ember-data/tests/integration/relationships/belongs-to-test.js b/packages/ember-data/tests/integration/relationships/belongs-to-test.js index 9679be315a2..db4ba5d33e7 100644 --- a/packages/ember-data/tests/integration/relationships/belongs-to-test.js +++ b/packages/ember-data/tests/integration/relationships/belongs-to-test.js @@ -71,7 +71,6 @@ module("integration/relationship/belongs_to Belongs-To Relationships", { author: Author }); - env.registry.optionsForType('serializer', { singleton: false }); env.registry.optionsForType('adapter', { singleton: false }); @@ -82,6 +81,14 @@ module("integration/relationship/belongs_to Belongs-To Relationships", { })); store = env.store; + + User = store.modelFor('user'); + Post = store.modelFor('post'); + Comment = store.modelFor('comment'); + Message = store.modelFor('message'); + Book = store.modelFor('book'); + Chapter = store.modelFor('chapter'); + Author = store.modelFor('author'); }, teardown: function() { @@ -227,7 +234,7 @@ test("A serializer can materialize a belongsTo as a link that gets sent back to }; env.adapter.findBelongsTo = async(function(store, snapshot, link, relationship) { - equal(relationship.type, Group); + equal(relationship.type, 'group'); equal(relationship.key, 'group'); equal(link, "/people/1/group"); @@ -380,7 +387,7 @@ test("relationshipsByName does not cache a factory", function() { // A model is looked up in the store based on a string, via user input var messageModelFromStore = store.modelFor('message'); // And the model is lookup up internally via the relationship type - var messageModelFromRelationType = store.modelFor(messageType.modelName); + var messageModelFromRelationType = store.modelFor(messageType); equal(messageModelFromRelationType, messageModelFromStore, "model factory based on relationship type matches the model based on store.modelFor"); diff --git a/packages/ember-data/tests/integration/relationships/has-many-test.js b/packages/ember-data/tests/integration/relationships/has-many-test.js index ad4981cfa39..42584542cd7 100644 --- a/packages/ember-data/tests/integration/relationships/has-many-test.js +++ b/packages/ember-data/tests/integration/relationships/has-many-test.js @@ -150,7 +150,7 @@ test("A serializer can materialize a hasMany as an opaque token that can be lazi env.adapter.findHasMany = function(store, snapshot, link, relationship) { equal(link, "/posts/1/comments", "findHasMany link was /posts/1/comments"); - equal(relationship.type.modelName, "comment", "relationship was passed correctly"); + equal(relationship.type, "comment", "relationship was passed correctly"); return Ember.RSVP.resolve([ { id: 1, body: "First" }, @@ -340,7 +340,7 @@ test("A hasMany relationship can be reloaded if it was fetched via a link", func }; env.adapter.findHasMany = function(store, snapshot, link, relationship) { - equal(relationship.type, Comment, "findHasMany relationship type was Comment"); + equal(relationship.type, 'comment', "findHasMany relationship type was Comment"); equal(relationship.key, 'comments', "findHasMany relationship key was comments"); equal(link, "/posts/1/comments", "findHasMany link was /posts/1/comments"); @@ -358,7 +358,7 @@ test("A hasMany relationship can be reloaded if it was fetched via a link", func equal(comments.get('length'), 2, "comments have 2 length"); env.adapter.findHasMany = function(store, snapshot, link, relationship) { - equal(relationship.type, Comment, "findHasMany relationship type was Comment"); + equal(relationship.type, 'comment', "findHasMany relationship type was Comment"); equal(relationship.key, 'comments', "findHasMany relationship key was comments"); equal(link, "/posts/1/comments", "findHasMany link was /posts/1/comments"); @@ -570,7 +570,7 @@ test("An updated `links` value should invalidate a relationship cache", function }); env.adapter.findHasMany = function(store, snapshot, link, relationship) { - equal(relationship.type.modelName, "comment", "relationship was passed correctly"); + equal(relationship.type, "comment", "relationship was passed correctly"); if (link === '/first') { return Ember.RSVP.resolve([ diff --git a/packages/ember-data/tests/unit/model/relationships-test.js b/packages/ember-data/tests/unit/model/relationships-test.js index f110aefd703..30dd0ec96e3 100644 --- a/packages/ember-data/tests/unit/model/relationships-test.js +++ b/packages/ember-data/tests/unit/model/relationships-test.js @@ -27,11 +27,11 @@ test("exposes a hash of the relationships on a model", function() { }); var relationships = get(Person, 'relationships'); - deepEqual(relationships.get(Person), [ + deepEqual(relationships.get('person'), [ { name: "people", kind: "hasMany" }, { name: "parent", kind: "belongsTo" } ]); - deepEqual(relationships.get(Occupation), [ + deepEqual(relationships.get('occupation'), [ { name: "occupations", kind: "hasMany" } ]); }); diff --git a/packages/ember-data/tests/unit/model/relationships/has-many-test.js b/packages/ember-data/tests/unit/model/relationships/has-many-test.js index a637bcab2f9..beb12f47d68 100644 --- a/packages/ember-data/tests/unit/model/relationships/has-many-test.js +++ b/packages/ember-data/tests/unit/model/relationships/has-many-test.js @@ -177,7 +177,7 @@ test("should be able to retrieve the type for a hasMany relationship without spe person: Person }); - equal(env.store.modelFor('person').typeForRelationship('tags'), Tag, "returns the relationship type"); + equal(env.store.modelFor('person').typeForRelationship('tags', env.store), Tag, "returns the relationship type"); }); test("should be able to retrieve the type for a hasMany relationship specified using a string from its metadata", function() { @@ -192,7 +192,7 @@ test("should be able to retrieve the type for a hasMany relationship specified u person: Person }); - equal(env.store.modelFor('person').typeForRelationship('tags'), Tag, "returns the relationship type"); + equal(env.store.modelFor('person').typeForRelationship('tags', env.store), Tag, "returns the relationship type"); }); test("should be able to retrieve the type for a belongsTo relationship without specifying a type from its metadata", function() { @@ -207,7 +207,7 @@ test("should be able to retrieve the type for a belongsTo relationship without s person: Person }); - equal(env.store.modelFor('person').typeForRelationship('tag'), Tag, "returns the relationship type"); + equal(env.store.modelFor('person').typeForRelationship('tag', env.store), Tag, "returns the relationship type"); }); test("should be able to retrieve the type for a belongsTo relationship specified using a string from its metadata", function() { @@ -224,7 +224,7 @@ test("should be able to retrieve the type for a belongsTo relationship specified person: Person }); - equal(env.store.modelFor('person').typeForRelationship('tags'), Tag, "returns the relationship type"); + equal(env.store.modelFor('person').typeForRelationship('tags', env.store), Tag, "returns the relationship type"); }); test("relationships work when declared with a string path", function() { diff --git a/packages/ember-data/tests/unit/store/create-record-test.js b/packages/ember-data/tests/unit/store/create-record-test.js index 1e555d6ac1a..9620ef2a1db 100644 --- a/packages/ember-data/tests/unit/store/create-record-test.js +++ b/packages/ember-data/tests/unit/store/create-record-test.js @@ -33,8 +33,8 @@ test("doesn't modify passed in properties hash", function() { test("allow passing relationships as well as attributes", function() { var records, storage; run(function() { - records = store.pushMany(Record, [{ id: 1, title: "it's a beautiful day" }, { id: 2, title: "it's a beautiful day" }]); - storage = store.createRecord(Storage, { name: 'Great store', records: records }); + records = store.pushMany('record', [{ id: 1, title: "it's a beautiful day" }, { id: 2, title: "it's a beautiful day" }]); + storage = store.createRecord('storage', { name: 'Great store', records: records }); }); equal(storage.get('name'), 'Great store', "The attribute is well defined"); From bcefa52774af9cbf8d9a7821664bde6452414628 Mon Sep 17 00:00:00 2001 From: Stanley Stuart Date: Thu, 28 May 2015 18:23:17 -0500 Subject: [PATCH 3/4] avoid unnecessary singularization for belongsTo relationships --- packages/ember-data/lib/system/relationship-meta.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/ember-data/lib/system/relationship-meta.js b/packages/ember-data/lib/system/relationship-meta.js index 3ec11c098f8..e2265acc260 100644 --- a/packages/ember-data/lib/system/relationship-meta.js +++ b/packages/ember-data/lib/system/relationship-meta.js @@ -5,7 +5,10 @@ export function typeForRelationshipMeta(meta) { var modelName; modelName = meta.type || meta.key; - return singularize(normalizeModelName(modelName)); + if (meta.kind === 'hasMany') { + modelName = singularize(normalizeModelName(modelName)); + } + return modelName; } export function relationshipFromMeta(meta) { From e074ce006c1ba0079493f1f7a220f3405b0820ff Mon Sep 17 00:00:00 2001 From: Stanley Stuart Date: Tue, 2 Jun 2015 19:32:47 -0500 Subject: [PATCH 4/4] Merge branch 'master' of github.com:emberjs/data into remove-passing-factories-to-store-methods --- .codeclimate.yml | 7 + Brocfile.js | 2 - bin/publish-to-s3.js | 4 +- config/s3ProjectConfig.js | 2 +- lib/amd-build.js | 4 - .../blueprints/ember-data/index.js | 1 + ...-inside-round-braces-in-call-expression.js | 2 - lib/lib-tree.js | 2 +- lib/test-tree.js | 4 +- .../ember-data/lib/adapters/rest-adapter.js | 8 +- packages/ember-data/lib/main.js | 3 + .../lib/serializers/embedded-records-mixin.js | 16 +- .../lib/serializers/rest-serializer.js | 46 +- packages/ember-data/lib/system/coerce-id.js | 9 + packages/ember-data/lib/system/many-array.js | 17 +- .../ember-data/lib/system/model/attributes.js | 16 +- .../lib/system/model/internal-model.js | 700 ++++++++++++++++++ packages/ember-data/lib/system/model/model.js | 543 +------------- .../ember-data/lib/system/model/states.js | 291 ++++---- .../lib/system/record-array-manager.js | 12 +- .../adapter-populated-record-array.js | 6 +- .../lib/system/record-arrays/record-array.js | 27 +- .../lib/system/relationships/belongs-to.js | 10 +- .../lib/system/relationships/has-many.js | 7 +- .../system/relationships/state/belongs-to.js | 20 +- .../lib/system/relationships/state/create.js | 2 +- .../system/relationships/state/has-many.js | 13 +- .../relationships/state/relationship.js | 6 +- packages/ember-data/lib/system/snapshot.js | 53 +- packages/ember-data/lib/system/store.js | 298 ++++---- .../ember-data/lib/system/store/finders.js | 70 +- .../integration/adapter/find-all-test.js | 4 + .../integration/adapter/rest-adapter-test.js | 2 +- .../integration/record-array-manager-test.js | 6 +- .../tests/integration/records/save-test.js | 15 +- .../relationships/belongs-to-test.js | 12 +- .../relationships/has-many-test.js | 18 +- .../embedded-records-mixin-test.js | 132 ++++ .../tests/integration/snapshot-test.js | 14 + packages/ember-data/tests/unit/model-test.js | 20 +- .../tests/unit/model/internal-model-test.js | 19 + .../unit/model/lifecycle-callbacks-test.js | 26 + .../ember-data/tests/unit/model/merge-test.js | 35 + .../model/relationships/record-array-test.js | 7 +- .../tests/unit/model/rollback-test.js | 45 ++ .../tests/unit/store/adapter-interop-test.js | 73 +- .../tests/unit/store/get-by-id-test.js | 35 + .../tests/unit/store/unload-test.js | 4 +- tests/ember-configuration.js | 2 +- tests/ember-data-setup.js | 2 - tests/index.html | 4 +- tests/qunit-configuration.js | 135 ++-- 52 files changed, 1717 insertions(+), 1094 deletions(-) create mode 100644 .codeclimate.yml create mode 100644 packages/ember-data/lib/system/coerce-id.js create mode 100644 packages/ember-data/lib/system/model/internal-model.js create mode 100644 packages/ember-data/tests/unit/model/internal-model-test.js create mode 100644 packages/ember-data/tests/unit/store/get-by-id-test.js diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 00000000000..05f8fac2da5 --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,7 @@ +# Save as .codeclimate.yml (note leading .) in project root directory +languages: + JavaScript: true + exclude_paths: + - "packages/activemodel-adapter/tests/*" + - "packages/ember/tests/*" + - "packages/ember-data/tests/*" diff --git a/Brocfile.js b/Brocfile.js index 92116e9158f..ad0c470a5b5 100644 --- a/Brocfile.js +++ b/Brocfile.js @@ -17,8 +17,6 @@ var version = require('git-repo-version')(10); var yuidoc = require('broccoli-yuidoc'); var replace = require('broccoli-replace'); var stew = require('broccoli-stew'); -var path = require('path'); -var fs = require('fs'); var babel = require('broccoli-babel-transpiler'); var babelOptions = require('./config/babel'); var fileCreator = require('broccoli-file-creator'); diff --git a/bin/publish-to-s3.js b/bin/publish-to-s3.js index bc1c9c0e0ce..7cda3058eb2 100755 --- a/bin/publish-to-s3.js +++ b/bin/publish-to-s3.js @@ -15,6 +15,6 @@ // ./bin/publish_to_s3.js // ``` var S3Publisher = require('ember-publisher'); -var configPath = require('path').join(__dirname, '../config/s3ProjectConfig.js') -publisher = new S3Publisher({projectConfigPath: configPath}); +var configPath = require('path').join(__dirname, '../config/s3ProjectConfig.js'); +var publisher = new S3Publisher({projectConfigPath: configPath}); publisher.publish(); diff --git a/config/s3ProjectConfig.js b/config/s3ProjectConfig.js index fa8c7239b24..e24fafd558c 100644 --- a/config/s3ProjectConfig.js +++ b/config/s3ProjectConfig.js @@ -6,7 +6,7 @@ function fileMap(revision,tag,date) { 'ember-data.prod.js': fileObject('ember-data.prod', '.js', 'text/javascript', revision, tag, date), 'docs/data.json': fileObject('ember-data-docs', '.json', 'application/json', revision, tag, date) }; -}; +} function fileObject(baseName, extension, contentType, currentRevision, tag, date){ var fullName = '/' + baseName + extension; diff --git a/lib/amd-build.js b/lib/amd-build.js index f9cc7a467ab..0fc58f3ae25 100644 --- a/lib/amd-build.js +++ b/lib/amd-build.js @@ -1,11 +1,7 @@ -var pickFiles = require('broccoli-static-compiler'); -var concat = require('broccoli-concat'); var es6 = require('broccoli-es6-module-transpiler'); var merge = require('broccoli-merge-trees'); var PackageResolver = require('es6-module-transpiler-package-resolver'); var AMDFormatter = require('es6-module-transpiler-amd-formatter'); -var fileCreator = require('broccoli-file-creator'); -var merge = require('broccoli-merge-trees'); var replace = require('broccoli-replace'); function amdES6Package(packages, root, outfile) { diff --git a/lib/ember-addon/blueprints/ember-data/index.js b/lib/ember-addon/blueprints/ember-data/index.js index 289382d65be..0de95eb439c 100644 --- a/lib/ember-addon/blueprints/ember-data/index.js +++ b/lib/ember-addon/blueprints/ember-data/index.js @@ -1,3 +1,4 @@ +/* jshint node: true */ 'use strict'; module.exports = { diff --git a/lib/jscs-rules/disallow-space-inside-round-braces-in-call-expression.js b/lib/jscs-rules/disallow-space-inside-round-braces-in-call-expression.js index 3685932f017..1245ee81680 100644 --- a/lib/jscs-rules/disallow-space-inside-round-braces-in-call-expression.js +++ b/lib/jscs-rules/disallow-space-inside-round-braces-in-call-expression.js @@ -35,8 +35,6 @@ module.exports.prototype = { }, check: function(file, errors) { - var tokens = file.getTokens(); - file.iterateNodesByType('CallExpression', function(node) { var nodeBeforeRoundBrace = node; diff --git a/lib/lib-tree.js b/lib/lib-tree.js index 3122aa868af..b140ea93168 100644 --- a/lib/lib-tree.js +++ b/lib/lib-tree.js @@ -6,4 +6,4 @@ module.exports = function libTree(tree) { srcDir: '/', destDir: '/' }); -} +}; diff --git a/lib/test-tree.js b/lib/test-tree.js index e48710e848a..eff7cfc54e4 100644 --- a/lib/test-tree.js +++ b/lib/test-tree.js @@ -4,7 +4,6 @@ var jshint = require('broccoli-jshint'); var path = require('path'); var wrap = require('broccoli-wrap'); var merge = require('broccoli-merge-trees'); -var amdBuild = require('./amd-build'); module.exports = function testTree(sourceTree, compiled) { var emberDataFiles = pickFiles(sourceTree, { @@ -29,8 +28,9 @@ module.exports = function testTree(sourceTree, compiled) { }; function hint(tree){ + var dirname = __dirname || path.resolve(path.dirname()); var jshinted = jshint(tree, { - jshintrcPath: path.join(__dirname, '..', '.jshintrc') + jshintrcPath: path.join(dirname, '..', '.jshintrc') }); return wrap(jshinted, { wrapper: [ "if (!QUnit.urlParams.nojshint) {\n", "\n}"], diff --git a/packages/ember-data/lib/adapters/rest-adapter.js b/packages/ember-data/lib/adapters/rest-adapter.js index 0d0e70b88c6..f6ee3f00e11 100644 --- a/packages/ember-data/lib/adapters/rest-adapter.js +++ b/packages/ember-data/lib/adapters/rest-adapter.js @@ -99,7 +99,7 @@ import BuildURLMixin from "ember-data/adapters/build-url-mixin"; property on the adapter: ```js - DS.RESTAdapter.reopen({ + App.ApplicationAdapter = DS.RESTAdapter.extend({ namespace: 'api/1' }); ``` @@ -110,7 +110,7 @@ import BuildURLMixin from "ember-data/adapters/build-url-mixin"; An adapter can target other hosts by setting the `host` property. ```js - DS.RESTAdapter.reopen({ + App.ApplicationAdapter = DS.RESTAdapter.extend({ host: 'https://api.example.com' }); ``` @@ -281,7 +281,7 @@ export default Adapter.extend(BuildURLMixin, { property on the adapter: ```javascript - DS.RESTAdapter.reopen({ + App.ApplicationAdapter = DS.RESTAdapter.extend({ namespace: 'api/1' }); ``` @@ -296,7 +296,7 @@ export default Adapter.extend(BuildURLMixin, { An adapter can target other hosts by setting the `host` property. ```javascript - DS.RESTAdapter.reopen({ + App.ApplicationAdapter = DS.RESTAdapter.extend({ host: 'https://api.example.com' }); ``` diff --git a/packages/ember-data/lib/main.js b/packages/ember-data/lib/main.js index 1def75f8607..d5a28b47a5a 100644 --- a/packages/ember-data/lib/main.js +++ b/packages/ember-data/lib/main.js @@ -19,6 +19,8 @@ import "ember-data/ext/date"; import normalizeModelName from "ember-data/system/normalize-model-name"; +import InternalModel from "ember-data/system/model/internal-model"; + import { PromiseArray, PromiseObject, @@ -85,6 +87,7 @@ DS.RootState = RootState; DS.attr = attr; DS.Errors = Errors; +DS.InternalModel = InternalModel; DS.Snapshot = Snapshot; DS.Adapter = Adapter; diff --git a/packages/ember-data/lib/serializers/embedded-records-mixin.js b/packages/ember-data/lib/serializers/embedded-records-mixin.js index 0e4a105755b..61789365942 100644 --- a/packages/ember-data/lib/serializers/embedded-records-mixin.js +++ b/packages/ember-data/lib/serializers/embedded-records-mixin.js @@ -1,4 +1,3 @@ -var get = Ember.get; var forEach = Ember.EnumerableUtils.forEach; var camelize = Ember.String.camelize; @@ -299,13 +298,17 @@ var EmbeddedRecordsMixin = Ember.Mixin.create({ } var includeIds = this.hasSerializeIdsOption(attr); var includeRecords = this.hasSerializeRecordsOption(attr); - var key; + var key, hasMany; if (includeIds) { key = this.keyForRelationship(attr, relationship.kind, 'serialize'); json[key] = snapshot.hasMany(attr, { ids: true }); } else if (includeRecords) { key = this.keyForAttribute(attr, 'serialize'); - json[key] = snapshot.hasMany(attr).map(function(embeddedSnapshot) { + hasMany = snapshot.hasMany(attr); + + Ember.warn("The embedded relationship '" + key + "' is undefined for '" + snapshot.modelName + "' with id '" + snapshot.id + "'. Please include it in your original payload.", Ember.typeOf(hasMany) !== 'undefined'); + + json[key] = Ember.A(hasMany).map(function(embeddedSnapshot) { var embeddedJson = embeddedSnapshot.record.serialize({ includeId: true }); this.removeEmbeddedForeignKey(snapshot, embeddedSnapshot, relationship, embeddedJson); return embeddedJson; @@ -442,11 +445,11 @@ function extractEmbeddedHasManyPolymorphic(store, key, hash) { var modelName = data.type; var embeddedSerializer = store.serializerFor(modelName); var embeddedTypeClass = store.modelFor(modelName); - var primaryKey = get(embeddedSerializer, 'primaryKey'); + // var primaryKey = embeddedSerializer.get('primaryKey'); var embeddedRecord = embeddedSerializer.normalize(embeddedTypeClass, data, null); store.push(embeddedTypeClass.modelName, embeddedRecord); - ids.push({ id: embeddedRecord[primaryKey], type: modelName }); + ids.push({ id: embeddedRecord.id, type: modelName }); }); hash[key] = ids; @@ -476,12 +479,11 @@ function extractEmbeddedBelongsToPolymorphic(store, key, hash) { var modelName = data.type; var embeddedSerializer = store.serializerFor(modelName); var embeddedTypeClass = store.modelFor(modelName); - var primaryKey = get(embeddedSerializer, 'primaryKey'); var embeddedRecord = embeddedSerializer.normalize(embeddedTypeClass, data, null); store.push(embeddedTypeClass.modelName, embeddedRecord); - hash[key] = embeddedRecord[primaryKey]; + hash[key] = embeddedRecord.id; hash[key + 'Type'] = modelName; return hash; } diff --git a/packages/ember-data/lib/serializers/rest-serializer.js b/packages/ember-data/lib/serializers/rest-serializer.js index 2a96622ac68..6743061b50e 100644 --- a/packages/ember-data/lib/serializers/rest-serializer.js +++ b/packages/ember-data/lib/serializers/rest-serializer.js @@ -5,15 +5,12 @@ import JSONSerializer from "ember-data/serializers/json-serializer"; import normalizeModelName from "ember-data/system/normalize-model-name"; import {singularize} from "ember-inflector/lib/system/string"; +import coerceId from "ember-data/system/coerce-id"; var forEach = Ember.ArrayPolyfills.forEach; var map = Ember.ArrayPolyfills.map; var camelize = Ember.String.camelize; -function coerceId(id) { - return id == null ? null : id + ''; -} - /** Normally, applications will use the `RESTSerializer` by implementing the `normalize` method and individual normalizations under @@ -263,7 +260,6 @@ var RESTSerializer = JSONSerializer.extend({ */ extractSingle: function(store, primaryTypeClass, rawPayload, recordId) { var payload = this.normalizePayload(rawPayload); - var primaryTypeClassName = primaryTypeClass.modelName; var primaryRecord; for (var prop in payload) { @@ -273,8 +269,8 @@ var RESTSerializer = JSONSerializer.extend({ Ember.warn(this.warnMessageNoModelForKey(prop, modelName), false); continue; } - var typeClass = store.modelFor(modelName); - var isPrimary = typeClass.modelName === primaryTypeClassName; + + var isPrimary = this.isPrimaryType(store, modelName, primaryTypeClass); var value = payload[prop]; if (value === null) { @@ -287,13 +283,10 @@ var RESTSerializer = JSONSerializer.extend({ continue; } - /*jshint loopfunc:true*/ - forEach.call(value, function(hash) { - var typeName = this.modelNameFromPayloadKey(prop); - var typeSerializer = store.serializerFor(typeName); - - hash = typeSerializer.normalize(typeClass, hash, prop); + var normalizedArray = this.normalizeArray(store, modelName, value, prop); + /*jshint loopfunc:true*/ + forEach.call(normalizedArray, function(hash) { var isFirstCreatedRecord = isPrimary && !recordId && !primaryRecord; var isUpdatedRecord = isPrimary && coerceId(hash.id) === recordId; @@ -416,7 +409,6 @@ var RESTSerializer = JSONSerializer.extend({ */ extractArray: function(store, primaryTypeClass, rawPayload) { var payload = this.normalizePayload(rawPayload); - var primaryTypeClassName = primaryTypeClass.modelName; var primaryArray; for (var prop in payload) { @@ -433,14 +425,9 @@ var RESTSerializer = JSONSerializer.extend({ Ember.warn(this.warnMessageNoModelForKey(prop, typeName), false); continue; } - var type = store.modelFor(typeName); - var typeSerializer = store.serializerFor(typeName); - var isPrimary = (!forcedSecondary && (type.modelName === primaryTypeClassName)); - /*jshint loopfunc:true*/ - var normalizedArray = map.call(payload[prop], function(hash) { - return typeSerializer.normalize(type, hash, prop); - }, this); + var normalizedArray = this.normalizeArray(store, typeName, payload[prop], prop); + var isPrimary = (!forcedSecondary && this.isPrimaryType(store, typeName, primaryTypeClass)); if (isPrimary) { primaryArray = normalizedArray; @@ -452,6 +439,21 @@ var RESTSerializer = JSONSerializer.extend({ return primaryArray; }, + normalizeArray: function(store, typeName, arrayHash, prop) { + var typeClass = store.modelFor(typeName); + var typeSerializer = store.serializerFor(typeName); + + /*jshint loopfunc:true*/ + return map.call(arrayHash, function(hash) { + return typeSerializer.normalize(typeClass, hash, prop); + }, this); + }, + + isPrimaryType: function(store, typeName, primaryTypeClass) { + var typeClass = store.modelFor(typeName); + return typeClass.modelName === primaryTypeClass.modelName; + }, + /** This method allows you to push a payload containing top-level collections of records organized per type. @@ -790,7 +792,7 @@ var RESTSerializer = JSONSerializer.extend({ }, /** - Deprecated. Use payloadKeyFromModelName instead + Deprecated. Use modelNameFromPayloadKey instead @method typeForRoot @param {String} modelName diff --git a/packages/ember-data/lib/system/coerce-id.js b/packages/ember-data/lib/system/coerce-id.js new file mode 100644 index 00000000000..62acf96bb14 --- /dev/null +++ b/packages/ember-data/lib/system/coerce-id.js @@ -0,0 +1,9 @@ +// Used by the store to normalize IDs entering the store. Despite the fact +// that developers may provide IDs as numbers (e.g., `store.find(Person, 1)`), +// it is important that internally we use strings, since IDs may be serialized +// and lose type information. For example, Ember's router may put a record's +// ID into the URL, and if we later try to deserialize that URL and find the +// corresponding record, we will not know if it is a string or a number. +export default function coerceId(id) { + return id == null ? null : id+''; +} diff --git a/packages/ember-data/lib/system/many-array.js b/packages/ember-data/lib/system/many-array.js index e4b3bbf15cc..9ac12443f91 100644 --- a/packages/ember-data/lib/system/many-array.js +++ b/packages/ember-data/lib/system/many-array.js @@ -6,6 +6,7 @@ import { PromiseArray } from "ember-data/system/promise-proxies"; var get = Ember.get; var set = Ember.set; var filter = Ember.ArrayPolyfills.filter; +var map = Ember.EnumerableUtils.map; /** A `ManyArray` is a `MutableArray` that represents the contents of a has-many @@ -57,19 +58,23 @@ export default Ember.Object.extend(Ember.MutableArray, Ember.Evented, { length: 0, objectAt: function(index) { - return this.currentState[index]; + //Ember observers such as 'firstObject', 'lastObject' might do out of bounds accesses + if (!this.currentState[index]) { + return undefined; + } + return this.currentState[index].getRecord(); }, flushCanonical: function() { //TODO make this smarter, currently its plenty stupid - var toSet = filter.call(this.canonicalState, function(record) { - return !record.get('isDeleted'); + var toSet = filter.call(this.canonicalState, function(internalModel) { + return !internalModel.isDeleted(); }); //a hack for not removing new records //TODO remove once we have proper diffing - var newRecords = this.currentState.filter(function(record) { - return record.get('isNew'); + var newRecords = this.currentState.filter(function(internalModel) { + return internalModel.isNew(); }); toSet = toSet.concat(newRecords); var oldLength = this.length; @@ -143,7 +148,7 @@ export default Ember.Object.extend(Ember.MutableArray, Ember.Evented, { this.get('relationship').removeRecords(records); } if (objects) { - this.get('relationship').addRecords(objects, idx); + this.get('relationship').addRecords(map(objects, function(obj) { return obj._internalModel; }), idx); } }, /** diff --git a/packages/ember-data/lib/system/model/attributes.js b/packages/ember-data/lib/system/model/attributes.js index 505d4bd38bd..a92615da32f 100644 --- a/packages/ember-data/lib/system/model/attributes.js +++ b/packages/ember-data/lib/system/model/attributes.js @@ -220,7 +220,7 @@ function getDefaultValue(record, options, key) { function hasValue(record, key) { return key in record._attributes || key in record._inFlightAttributes || - record._data.hasOwnProperty(key); + key in record._data; } function getValue(record, key) { @@ -300,25 +300,27 @@ export default function attr(type, options) { return computedPolyfill({ get: function(key) { - if (hasValue(this, key)) { - return getValue(this, key); + var internalModel = this._internalModel; + if (hasValue(internalModel, key)) { + return getValue(internalModel, key); } else { return getDefaultValue(this, options, key); } }, set: function(key, value) { Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('')` from " + this.constructor.toString(), key !== 'id'); - var oldValue = getValue(this, key); + var internalModel = this._internalModel; + var oldValue = getValue(internalModel, key); if (value !== oldValue) { // Add the new value to the changed attributes hash; it will get deleted by // the 'didSetProperty' handler if it is no different from the original value - this._attributes[key] = value; + internalModel._attributes[key] = value; - this.send('didSetProperty', { + this._internalModel.send('didSetProperty', { name: key, oldValue: oldValue, - originalValue: this._data[key], + originalValue: internalModel._data[key], value: value }); } diff --git a/packages/ember-data/lib/system/model/internal-model.js b/packages/ember-data/lib/system/model/internal-model.js new file mode 100644 index 00000000000..8305d3da021 --- /dev/null +++ b/packages/ember-data/lib/system/model/internal-model.js @@ -0,0 +1,700 @@ +import merge from "ember-data/system/merge"; +import RootState from "ember-data/system/model/states"; +import createRelationshipFor from "ember-data/system/relationships/state/create"; +import Snapshot from "ember-data/system/snapshot"; +import Errors from "ember-data/system/model/errors"; + +var Promise = Ember.RSVP.Promise; +var get = Ember.get; +var set = Ember.set; +var forEach = Ember.ArrayPolyfills.forEach; +var map = Ember.ArrayPolyfills.map; + +var _extractPivotNameCache = Ember.create(null); +var _splitOnDotCache = Ember.create(null); + +function splitOnDot(name) { + return _splitOnDotCache[name] || ( + _splitOnDotCache[name] = name.split('.') + ); +} + +function extractPivotName(name) { + return _extractPivotNameCache[name] || ( + _extractPivotNameCache[name] = splitOnDot(name)[0] + ); +} + +function retrieveFromCurrentState(key) { + return function() { + return get(this.currentState, key); + }; +} + +/** + `InternalModel` is the Model class that we use internally inside Ember Data to represent models. + Internal ED methods should only deal with `InternalModel` objects. It is a fast, plain Javascript class. + + We expose `DS.Model` to application code, by materializing a `DS.Model` from `InternalModel` lazily, as + a performance optimization. + + `InternalModel` should never be exposed to application code. At the boundaries of the system, in places + like `find`, `push`, etc. we convert between Models and InternalModels. + + We need to make sure that the properties from `InternalModel` are correctly exposed/proxied on `Model` + if they are needed. + + @class InternalModel +*/ + +var InternalModel = function InternalModel(type, id, store, container, data) { + this.type = type; + this.id = id; + this.store = store; + this.container = container; + this._data = data || Ember.create(null); + this.modelName = type.modelName; + this.errors = null; + this.dataHasInitialized = false; + //Look into making this lazy + this._deferredTriggers = []; + this._attributes = Ember.create(null); + this._inFlightAttributes = Ember.create(null); + this._relationships = Ember.create(null); + this.currentState = RootState.empty; + this.isReloading = false; + /* + implicit relationships are relationship which have not been declared but the inverse side exists on + another record somewhere + For example if there was + ``` + App.Comment = DS.Model.extend({ + name: DS.attr() + }) + ``` + but there is also + ``` + App.Post = DS.Model.extend({ + name: DS.attr(), + comments: DS.hasMany('comment') + }) + ``` + + would have a implicit post relationship in order to be do things like remove ourselves from the post + when we are deleted + */ + this._implicitRelationships = Ember.create(null); + var model = this; + //TODO Move into a getter for better perf + this.eachRelationship(function(key, descriptor) { + model._relationships[key] = createRelationshipFor(model, descriptor, model.store); + }); +}; + +InternalModel.prototype = { + isEmpty: retrieveFromCurrentState('isEmpty'), + isLoading: retrieveFromCurrentState('isLoading'), + isLoaded: retrieveFromCurrentState('isLoaded'), + isDirty: retrieveFromCurrentState('isDirty'), + isSaving: retrieveFromCurrentState('isSaving'), + isDeleted: retrieveFromCurrentState('isDeleted'), + isNew: retrieveFromCurrentState('isNew'), + isValid: retrieveFromCurrentState('isValid'), + dirtyType: retrieveFromCurrentState('dirtyType'), + + constructor: InternalModel, + materializeRecord: function() { + Ember.assert("Materialized " + this.modelName + " record with id:" + this.id + "more than once", this.record === null || this.record === undefined); + // lookupFactory should really return an object that creates + // instances with the injections applied + this.record = this.type._create({ + id: this.id, + store: this.store, + container: this.container + }); + this.record._internalModel = this; + this._triggerDeferredTriggers(); + }, + + recordObjectWillDestroy: function() { + this.record = null; + }, + + deleteRecord: function() { + this.send('deleteRecord'); + }, + + save: function() { + var promiseLabel = "DS: Model#save " + this; + var resolver = Ember.RSVP.defer(promiseLabel); + + this.store.scheduleSave(this, resolver); + return resolver.promise; + }, + + startedReloading: function() { + this.isReloading = true; + if (this.record) { + set(this.record, 'isReloading', true); + } + }, + + finishedReloading: function() { + this.isReloading = false; + if (this.record) { + set(this.record, 'isReloading', false); + } + }, + + reload: function() { + this.startedReloading(); + var record = this; + var promiseLabel = "DS: Model#reload of " + this; + return new Promise(function(resolve) { + record.send('reloadRecord', resolve); + }, promiseLabel).then(function() { + record.didCleanError(); + return record; + }, function(reason) { + record.didError(); + throw reason; + }, "DS: Model#reload complete, update flags").finally(function () { + record.finishedReloading(); + record.updateRecordArrays(); + }); + }, + + getRecord: function() { + if (!this.record) { + this.materializeRecord(); + } + return this.record; + }, + + unloadRecord: function() { + this.send('unloadRecord'); + }, + + eachRelationship: function(callback, binding) { + return this.type.eachRelationship(callback, binding); + }, + + eachAttribute: function(callback, binding) { + return this.type.eachAttribute(callback, binding); + }, + + inverseFor: function(key) { + return this.type.inverseFor(key); + }, + + setupData: function(data) { + var changedKeys = mergeAndReturnChangedKeys(this._data, data); + this.pushedData(); + if (this.record) { + this.record._notifyProperties(changedKeys); + } + this.didInitalizeData(); + }, + + becameReady: function() { + Ember.run.schedule('actions', this.store.recordArrayManager, this.store.recordArrayManager.recordWasLoaded, this); + }, + + didInitalizeData: function() { + if (!this.dataHasInitialized) { + this.becameReady(); + this.dataHasInitialized = true; + } + }, + + destroy: function() { + if (this.record) { + return this.record.destroy(); + } + }, + + /** + @method createSnapshot + @private + */ + createSnapshot: function() { + return new Snapshot(this); + }, + + /** + @method loadingData + @private + @param {Promise} promise + */ + loadingData: function(promise) { + this.send('loadingData', promise); + }, + + /** + @method loadedData + @private + */ + loadedData: function() { + this.send('loadedData'); + this.didInitalizeData(); + }, + + /** + @method notFound + @private + */ + notFound: function() { + this.send('notFound'); + }, + + /** + @method pushedData + @private + */ + pushedData: function() { + this.send('pushedData'); + }, + + flushChangedAttributes: function() { + this._inFlightAttributes = this._attributes; + this._attributes = Ember.create(null); + }, + + /** + @method adapterWillCommit + @private + */ + adapterWillCommit: function() { + this.send('willCommit'); + }, + + /** + @method adapterDidDirty + @private + */ + adapterDidDirty: function() { + this.send('becomeDirty'); + this.updateRecordArraysLater(); + }, + + /** + @method send + @private + @param {String} name + @param {Object} context + */ + send: function(name, context) { + var currentState = get(this, 'currentState'); + + if (!currentState[name]) { + this._unhandledEvent(currentState, name, context); + } + + return currentState[name](this, context); + }, + + notifyHasManyAdded: function(key, record, idx) { + if (this.record) { + this.record.notifyHasManyAdded(key, record, idx); + } + }, + + notifyHasManyRemoved: function(key, record, idx) { + if (this.record) { + this.record.notifyHasManyRemoved(key, record, idx); + } + }, + + notifyBelongsToChanged: function(key, record) { + if (this.record) { + this.record.notifyBelongsToChanged(key, record); + } + }, + + notifyPropertyChange: function(key) { + if (this.record) { + this.record.notifyPropertyChange(key); + } + }, + + rollback: function() { + var dirtyKeys = Ember.keys(this._attributes); + + this._attributes = Ember.create(null); + + if (get(this, 'isError')) { + this._inFlightAttributes = Ember.create(null); + this.didCleanError(); + } + + //Eventually rollback will always work for relationships + //For now we support it only out of deleted state, because we + //have an explicit way of knowing when the server acked the relationship change + if (this.isDeleted()) { + //TODO: Should probably move this to the state machine somehow + this.becameReady(); + this.reconnectRelationships(); + } + + if (this.isNew()) { + this.clearRelationships(); + } + + if (this.isValid()) { + this._inFlightAttributes = Ember.create(null); + } + + this.send('rolledBack'); + + this.record._notifyProperties(dirtyKeys); + + }, + /** + @method transitionTo + @private + @param {String} name + */ + transitionTo: function(name) { + // POSSIBLE TODO: Remove this code and replace with + // always having direct reference to state objects + + var pivotName = extractPivotName(name); + var currentState = get(this, 'currentState'); + var state = currentState; + + do { + if (state.exit) { state.exit(this); } + state = state.parentState; + } while (!state.hasOwnProperty(pivotName)); + + var path = splitOnDot(name); + var setups = []; + var enters = []; + var i, l; + + for (i=0, l=path.length; i { + recordErrors.add(key, errors[key]); + }); + this._saveWasRejected(); + }, + + /** + @method adapterDidError + @private + */ + adapterDidError: function() { + this.send('becameError'); + this.didError(); + this._saveWasRejected(); + }, + + _saveWasRejected: function() { + var keys = Ember.keys(this._inFlightAttributes); + for (var i=0; i < keys.length; i++) { + if (this._attributes[keys[i]] === undefined) { + this._attributes[keys[i]] = this._inFlightAttributes[keys[i]]; + } + } + this._inFlightAttributes = Ember.create(null); + }, + + toString: function() { + if (this.record) { + return this.record.toString(); + } else { + return `<${this.modelName}:${this.id}>`; + } + } +}; + +// Like Ember.merge, but instead returns a list of keys +// for values that fail a strict equality check +// instead of the original object. +function mergeAndReturnChangedKeys(original, updates) { + var changedKeys = []; + + if (!updates || typeof updates !== 'object') { + return changedKeys; + } + + var keys = Ember.keys(updates); + var length = keys.length; + var i, val, key; + + for (i = 0; i < length; i++) { + key = keys[i]; + val = updates[key]; + + if (original[key] !== val) { + changedKeys.push(key); + } + + original[key] = val; + } + return changedKeys; +} + +export default InternalModel; diff --git a/packages/ember-data/lib/system/model/model.js b/packages/ember-data/lib/system/model/model.js index fbdbe98011f..60380426283 100644 --- a/packages/ember-data/lib/system/model/model.js +++ b/packages/ember-data/lib/system/model/model.js @@ -1,74 +1,27 @@ -import RootState from "ember-data/system/model/states"; -import Errors from "ember-data/system/model/errors"; import { PromiseObject } from "ember-data/system/promise-proxies"; -import merge from "ember-data/system/merge"; import JSONSerializer from "ember-data/serializers/json-serializer"; -import createRelationshipFor from "ember-data/system/relationships/state/create"; -import Snapshot from "ember-data/system/snapshot"; /** @module ember-data */ var get = Ember.get; -var set = Ember.set; -var Promise = Ember.RSVP.Promise; -var forEach = Ember.ArrayPolyfills.forEach; -var map = Ember.ArrayPolyfills.map; var intersection = Ember.EnumerableUtils.intersection; var RESERVED_MODEL_PROPS = [ 'currentState', 'data', 'store' ]; var retrieveFromCurrentState = Ember.computed('currentState', function(key) { - return get(get(this, 'currentState'), key); + return get(this._internalModel.currentState, key); }).readOnly(); -var _extractPivotNameCache = Ember.create(null); -var _splitOnDotCache = Ember.create(null); - -function splitOnDot(name) { - return _splitOnDotCache[name] || ( - (_splitOnDotCache[name] = name.split('.')) - ); -} - -function extractPivotName(name) { - return _extractPivotNameCache[name] || ( - (_extractPivotNameCache[name] = splitOnDot(name)[0]) - ); -} - -// Like Ember.merge, but instead returns a list of keys -// for values that fail a strict equality check -// instead of the original object. -function mergeAndReturnChangedKeys(original, updates) { - var changedKeys = []; - - if (!updates || typeof updates !== 'object') { - return changedKeys; - } - - var keys = Ember.keys(updates); - var length = keys.length; - var i, val, key; - - for (i = 0; i < length; i++) { - key = keys[i]; - val = updates[key]; - - if (original[key] !== val) { - changedKeys.push(key); - } - - original[key] = val; - } - return changedKeys; -} - /** The model class that all Ember Data records descend from. + This is the public API of Ember Data models. If you are using Ember Data + in your application, this is the class you should use. + If you are working on Ember Data internals, you most likely want to be dealing + with `InternalModel` @class Model @namespace DS @@ -288,6 +241,7 @@ var Model = Ember.Object.extend(Ember.Evented, { @readOnly */ isError: false, + /** If `true` the store is attempting to reload the record form the adapter. @@ -305,17 +259,6 @@ var Model = Ember.Object.extend(Ember.Evented, { */ isReloading: false, - /** - The `clientId` property is a transient numerical identifier - generated at runtime by the data store. It is important - primarily because newly created objects may not yet have an - externally generated id. - - @property clientId - @private - @type {Number|String} - */ - clientId: null, /** All ember models have an id property. This is an identifier managed by an external source. These are always coerced to be @@ -342,7 +285,6 @@ var Model = Ember.Object.extend(Ember.Evented, { @private @type {Object} */ - currentState: RootState.empty, /** When the record is in the `invalid` state this object will contain @@ -396,15 +338,7 @@ var Model = Ember.Object.extend(Ember.Evented, { @type {DS.Errors} */ errors: Ember.computed(function() { - var errors = Errors.create(); - - errors.registerHandlers(this, function() { - this.send('becameInvalid'); - }, function() { - this.send('becameValid'); - }); - - return errors; + return this._internalModel.getErrors(); }).readOnly(), /** @@ -442,7 +376,7 @@ var Model = Ember.Object.extend(Ember.Evented, { toJSON: function(options) { // container is for lazy transform lookups var serializer = JSONSerializer.create({ container: this.container }); - var snapshot = this._createSnapshot(); + var snapshot = this._internalModel.createSnapshot(); return serializer.serialize(snapshot, options); }, @@ -453,9 +387,8 @@ var Model = Ember.Object.extend(Ember.Evented, { @event ready */ - ready: function() { - this.store.recordArrayManager.recordWasLoaded(this); - }, + ready: Ember.K, + /** Fired when the record is loaded from the server. @@ -510,54 +443,9 @@ var Model = Ember.Object.extend(Ember.Evented, { @private @type {Object} */ - data: Ember.computed(function() { - this._data = this._data || {}; - return this._data; - }).readOnly(), - - _data: null, - - init: function() { - this._super.apply(this, arguments); - this._setup(); - }, - - _setup: function() { - this._changesToSync = {}; - this._deferredTriggers = []; - this._data = {}; - this._attributes = Ember.create(null); - this._inFlightAttributes = Ember.create(null); - this._relationships = {}; - /* - implicit relationships are relationship which have not been declared but the inverse side exists on - another record somewhere - For example if there was - ``` - App.Comment = DS.Model.extend({ - name: DS.attr() - }) - ``` - but there is also - ``` - App.Post = DS.Model.extend({ - name: DS.attr(), - comments: DS.hasMany('comment') - }) - ``` - - would have a implicit post relationship in order to be do things like remove ourselves from the post - when we are deleted - */ - this._implicitRelationships = Ember.create(null); - var model = this; - //TODO Move into a getter for better perf - this.constructor.eachRelationship(function(key, descriptor) { - model._relationships[key] = createRelationshipFor(model, descriptor, model.store); - }); - - }, + data: Ember.computed.readOnly('_internalModel._data'), + //TODO Do we want to deprecate these? /** @method send @private @@ -565,13 +453,7 @@ var Model = Ember.Object.extend(Ember.Evented, { @param {Object} context */ send: function(name, context) { - var currentState = get(this, 'currentState'); - - if (!currentState[name]) { - this._unhandledEvent(currentState, name, context); - } - - return currentState[name](this, context); + return this._internalModel.send(name, context); }, /** @@ -580,92 +462,9 @@ var Model = Ember.Object.extend(Ember.Evented, { @param {String} name */ transitionTo: function(name) { - // POSSIBLE TODO: Remove this code and replace with - // always having direct references to state objects - - var pivotName = extractPivotName(name); - var currentState = get(this, 'currentState'); - var state = currentState; - - do { - if (state.exit) { state.exit(this); } - state = state.parentState; - } while (!state.hasOwnProperty(pivotName)); - - var path = splitOnDot(name); - var setups = []; - var enters = []; - var i, l; - - for (i=0, l=path.length; i 0; - if (!stillDirty) { record.send('rolledBack'); } + if (!stillDirty) { internalModel.send('rolledBack'); } }, pushedData: Ember.K, becomeDirty: Ember.K, - willCommit: function(record) { - record.transitionTo('inFlight'); + willCommit: function(internalModel) { + internalModel.transitionTo('inFlight'); }, - reloadRecord: function(record, resolve) { - resolve(get(record, 'store').reloadRecord(record)); + reloadRecord: function(internalModel, resolve) { + resolve(internalModel.store.reloadRecord(internalModel)); }, - rolledBack: function(record) { - record.transitionTo('loaded.saved'); + rolledBack: function(internalModel) { + internalModel.transitionTo('loaded.saved'); }, - becameInvalid: function(record) { - record.transitionTo('invalid'); + becameInvalid: function(internalModel) { + internalModel.transitionTo('invalid'); }, - rollback: function(record) { - record.rollback(); - record.triggerLater('ready'); + rollback: function(internalModel) { + internalModel.rollback(); + internalModel.triggerLater('ready'); } }, @@ -291,28 +290,26 @@ var DirtyState = { becomeDirty: Ember.K, pushedData: Ember.K, - unloadRecord: function(record) { - Ember.assert("You can only unload a record which is not inFlight. `" + Ember.inspect(record) + " `", false); - }, + unloadRecord: assertAgainstUnloadRecord, // TODO: More robust semantics around save-while-in-flight willCommit: Ember.K, - didCommit: function(record) { + didCommit: function(internalModel) { var dirtyType = get(this, 'dirtyType'); - record.transitionTo('saved'); - record.send('invokeLifecycleCallbacks', dirtyType); + internalModel.transitionTo('saved'); + internalModel.send('invokeLifecycleCallbacks', dirtyType); }, - becameInvalid: function(record) { - record.transitionTo('invalid'); - record.send('invokeLifecycleCallbacks'); + becameInvalid: function(internalModel) { + internalModel.transitionTo('invalid'); + internalModel.send('invokeLifecycleCallbacks'); }, - becameError: function(record) { - record.transitionTo('uncommitted'); - record.triggerLater('becameError', record); + becameError: function(internalModel) { + internalModel.transitionTo('uncommitted'); + internalModel.triggerLater('becameError', internalModel); } }, @@ -323,39 +320,39 @@ var DirtyState = { isValid: false, // EVENTS - deleteRecord: function(record) { - record.transitionTo('deleted.uncommitted'); - record.disconnectRelationships(); + deleteRecord: function(internalModel) { + internalModel.transitionTo('deleted.uncommitted'); + internalModel.disconnectRelationships(); }, - didSetProperty: function(record, context) { - get(record, 'errors').remove(context.name); + didSetProperty: function(internalModel, context) { + internalModel.getErrors().remove(context.name); - didSetProperty(record, context); + didSetProperty(internalModel, context); }, becomeDirty: Ember.K, - willCommit: function(record) { - get(record, 'errors').clear(); - record.transitionTo('inFlight'); + willCommit: function(internalModel) { + internalModel.getErrors().clear(); + internalModel.transitionTo('inFlight'); }, - rolledBack: function(record) { - get(record, 'errors').clear(); - record.triggerLater('ready'); + rolledBack: function(internalModel) { + internalModel.getErrors().clear(); + internalModel.triggerLater('ready'); }, - becameValid: function(record) { - record.transitionTo('uncommitted'); + becameValid: function(internalModel) { + internalModel.transitionTo('uncommitted'); }, - invokeLifecycleCallbacks: function(record) { - record.triggerLater('becameInvalid', record); + invokeLifecycleCallbacks: function(internalModel) { + internalModel.triggerLater('becameInvalid', internalModel); }, - exit: function(record) { - record._inFlightAttributes = {}; + exit: function(internalModel) { + internalModel._inFlightAttributes = Ember.create(null); } } }; @@ -399,41 +396,44 @@ var createdState = dirtyState({ isNew: true }); -createdState.uncommitted.rolledBack = function(record) { - record.transitionTo('deleted.saved'); +createdState.invalid.rolledBack = function(internalModel) { + internalModel.transitionTo('deleted.saved'); +}; +createdState.uncommitted.rolledBack = function(internalModel) { + internalModel.transitionTo('deleted.saved'); }; var updatedState = dirtyState({ dirtyType: 'updated' }); -createdState.uncommitted.deleteRecord = function(record) { - record.disconnectRelationships(); - record.transitionTo('deleted.saved'); - record.send('invokeLifecycleCallbacks'); +createdState.uncommitted.deleteRecord = function(internalModel) { + internalModel.disconnectRelationships(); + internalModel.transitionTo('deleted.saved'); + internalModel.send('invokeLifecycleCallbacks'); }; -createdState.uncommitted.rollback = function(record) { +createdState.uncommitted.rollback = function(internalModel) { DirtyState.uncommitted.rollback.apply(this, arguments); - record.transitionTo('deleted.saved'); + internalModel.transitionTo('deleted.saved'); }; -createdState.uncommitted.pushedData = function(record) { - record.transitionTo('loaded.updated.uncommitted'); - record.triggerLater('didLoad'); +createdState.uncommitted.pushedData = function(internalModel) { + internalModel.transitionTo('loaded.updated.uncommitted'); + internalModel.triggerLater('didLoad'); }; createdState.uncommitted.propertyWasReset = Ember.K; -function assertAgainstUnloadRecord(record) { - Ember.assert("You can only unload a record which is not inFlight. `" + Ember.inspect(record) + "`", false); +function assertAgainstUnloadRecord(internalModel) { + Ember.assert("You can only unload a record which is not inFlight. `" + internalModel + "`", false); } updatedState.inFlight.unloadRecord = assertAgainstUnloadRecord; -updatedState.uncommitted.deleteRecord = function(record) { - record.transitionTo('deleted.uncommitted'); - record.disconnectRelationships(); +updatedState.uncommitted.deleteRecord = function(internalModel) { + internalModel.transitionTo('deleted.uncommitted'); + internalModel.disconnectRelationships(); }; var RootState = { @@ -454,11 +454,11 @@ var RootState = { // in-flight state, rolling back the record doesn't move // you out of the in-flight state. rolledBack: Ember.K, - unloadRecord: function(record) { + unloadRecord: function(internalModel) { // clear relationships before moving to deleted state // otherwise it fails - record.clearRelationships(); - record.transitionTo('deleted.saved'); + internalModel.clearRelationships(); + internalModel.transitionTo('deleted.saved'); }, @@ -475,20 +475,20 @@ var RootState = { isEmpty: true, // EVENTS - loadingData: function(record, promise) { - record._loadingPromise = promise; - record.transitionTo('loading'); + loadingData: function(internalModel, promise) { + internalModel._loadingPromise = promise; + internalModel.transitionTo('loading'); }, - loadedData: function(record) { - record.transitionTo('loaded.created.uncommitted'); - record.triggerLater('ready'); + loadedData: function(internalModel) { + internalModel.transitionTo('loaded.created.uncommitted'); + internalModel.triggerLater('ready'); }, - pushedData: function(record) { - record.transitionTo('loaded.saved'); - record.triggerLater('didLoad'); - record.triggerLater('ready'); + pushedData: function(internalModel) { + internalModel.transitionTo('loaded.saved'); + internalModel.triggerLater('didLoad'); + internalModel.triggerLater('ready'); } }, @@ -502,24 +502,25 @@ var RootState = { // FLAGS isLoading: true, - exit: function(record) { - record._loadingPromise = null; + exit: function(internalModel) { + internalModel._loadingPromise = null; }, // EVENTS - pushedData: function(record) { - record.transitionTo('loaded.saved'); - record.triggerLater('didLoad'); - record.triggerLater('ready'); - set(record, 'isError', false); + pushedData: function(internalModel) { + internalModel.transitionTo('loaded.saved'); + internalModel.triggerLater('didLoad'); + internalModel.triggerLater('ready'); + //TODO this seems out of place here + internalModel.didCleanError(); }, - becameError: function(record) { - record.triggerLater('becameError', record); + becameError: function(internalModel) { + internalModel.triggerLater('becameError', internalModel); }, - notFound: function(record) { - record.transitionTo('empty'); + notFound: function(internalModel) { + internalModel.transitionTo('empty'); } }, @@ -541,12 +542,12 @@ var RootState = { // If there are no local changes to a record, it remains // in the `saved` state. saved: { - setup: function(record) { - var attrs = record._attributes; + setup: function(internalModel) { + var attrs = internalModel._attributes; var isDirty = Ember.keys(attrs).length > 0; if (isDirty) { - record.adapterDidDirty(); + internalModel.adapterDidDirty(); } }, @@ -555,32 +556,32 @@ var RootState = { pushedData: Ember.K, - becomeDirty: function(record) { - record.transitionTo('updated.uncommitted'); + becomeDirty: function(internalModel) { + internalModel.transitionTo('updated.uncommitted'); }, - willCommit: function(record) { - record.transitionTo('updated.inFlight'); + willCommit: function(internalModel) { + internalModel.transitionTo('updated.inFlight'); }, - reloadRecord: function(record, resolve) { - resolve(get(record, 'store').reloadRecord(record)); + reloadRecord: function(internalModel, resolve) { + resolve(internalModel.store.reloadRecord(internalModel)); }, - deleteRecord: function(record) { - record.transitionTo('deleted.uncommitted'); - record.disconnectRelationships(); + deleteRecord: function(internalModel) { + internalModel.transitionTo('deleted.uncommitted'); + internalModel.disconnectRelationships(); }, - unloadRecord: function(record) { + unloadRecord: function(internalModel) { // clear relationships before moving to deleted state // otherwise it fails - record.clearRelationships(); - record.transitionTo('deleted.saved'); + internalModel.clearRelationships(); + internalModel.transitionTo('deleted.saved'); }, - didCommit: function(record) { - record.send('invokeLifecycleCallbacks', get(record, 'lastDirtyType')); + didCommit: function(internalModel) { + internalModel.send('invokeLifecycleCallbacks', get(internalModel, 'lastDirtyType')); }, // loaded.saved.notFound would be triggered by a failed @@ -611,8 +612,8 @@ var RootState = { isDirty: true, // TRANSITIONS - setup: function(record) { - record.updateRecordArrays(); + setup: function(internalModel) { + internalModel.updateRecordArrays(); }, // SUBSTATES @@ -624,22 +625,22 @@ var RootState = { // EVENTS - willCommit: function(record) { - record.transitionTo('inFlight'); + willCommit: function(internalModel) { + internalModel.transitionTo('inFlight'); }, - rollback: function(record) { - record.rollback(); - record.triggerLater('ready'); + rollback: function(internalModel) { + internalModel.rollback(); + internalModel.triggerLater('ready'); }, pushedData: Ember.K, becomeDirty: Ember.K, deleteRecord: Ember.K, - rolledBack: function(record) { - record.transitionTo('loaded.saved'); - record.triggerLater('ready'); + rolledBack: function(internalModel) { + internalModel.transitionTo('loaded.saved'); + internalModel.triggerLater('ready'); } }, @@ -657,20 +658,20 @@ var RootState = { // TODO: More robust semantics around save-while-in-flight willCommit: Ember.K, - didCommit: function(record) { - record.transitionTo('saved'); + didCommit: function(internalModel) { + internalModel.transitionTo('saved'); - record.send('invokeLifecycleCallbacks'); + internalModel.send('invokeLifecycleCallbacks'); }, - becameError: function(record) { - record.transitionTo('uncommitted'); - record.triggerLater('becameError', record); + becameError: function(internalModel) { + internalModel.transitionTo('uncommitted'); + internalModel.triggerLater('becameError', internalModel); }, - becameInvalid: function(record) { - record.transitionTo('invalid'); - record.triggerLater('becameInvalid', record); + becameInvalid: function(internalModel) { + internalModel.transitionTo('invalid'); + internalModel.triggerLater('becameInvalid', internalModel); } }, @@ -681,14 +682,14 @@ var RootState = { // FLAGS isDirty: false, - setup: function(record) { - var store = get(record, 'store'); - store._dematerializeRecord(record); + setup: function(internalModel) { + var store = internalModel.store; + store._dematerializeRecord(internalModel); }, - invokeLifecycleCallbacks: function(record) { - record.triggerLater('didDelete', record); - record.triggerLater('didCommit', record); + invokeLifecycleCallbacks: function(internalModel) { + internalModel.triggerLater('didDelete', internalModel); + internalModel.triggerLater('didCommit', internalModel); }, willCommit: Ember.K, @@ -699,10 +700,10 @@ var RootState = { invalid: { isValid: false, - didSetProperty: function(record, context) { - get(record, 'errors').remove(context.name); + didSetProperty: function(internalModel, context) { + internalModel.getErrors().remove(context.name); - didSetProperty(record, context); + didSetProperty(internalModel, context); }, deleteRecord: Ember.K, @@ -710,27 +711,27 @@ var RootState = { willCommit: Ember.K, - rolledBack: function(record) { - get(record, 'errors').clear(); - record.transitionTo('loaded.saved'); - record.triggerLater('ready'); + rolledBack: function(internalModel) { + internalModel.getErrors().clear(); + internalModel.transitionTo('loaded.saved'); + internalModel.triggerLater('ready'); }, - becameValid: function(record) { - record.transitionTo('uncommitted'); + becameValid: function(internalModel) { + internalModel.transitionTo('uncommitted'); } } }, - invokeLifecycleCallbacks: function(record, dirtyType) { + invokeLifecycleCallbacks: function(internalModel, dirtyType) { if (dirtyType === 'created') { - record.triggerLater('didCreate', record); + internalModel.triggerLater('didCreate', internalModel); } else { - record.triggerLater('didUpdate', record); + internalModel.triggerLater('didUpdate', internalModel); } - record.triggerLater('didCommit', record); + internalModel.triggerLater('didCommit', internalModel); } }; diff --git a/packages/ember-data/lib/system/record-array-manager.js b/packages/ember-data/lib/system/record-array-manager.js index 5fb9cb88303..609e90c7275 100644 --- a/packages/ember-data/lib/system/record-array-manager.js +++ b/packages/ember-data/lib/system/record-array-manager.js @@ -54,7 +54,7 @@ export default Ember.Object.extend({ */ updateRecordArrays: function() { forEach(this.changedRecords, function(record) { - if (get(record, 'isDeleted')) { + if (record.isDeleted()) { this._recordWasDeleted(record); } else { this._recordWasChanged(record); @@ -79,7 +79,7 @@ export default Ember.Object.extend({ //Don't need to update non filtered arrays on simple changes _recordWasChanged: function (record) { - var typeClass = record.constructor; + var typeClass = record.type; var recordArrays = this.filteredRecordArrays.get(typeClass); var filter; @@ -93,7 +93,7 @@ export default Ember.Object.extend({ //Need to update live arrays on loading recordWasLoaded: function(record) { - var typeClass = record.constructor; + var typeClass = record.type; var recordArrays = this.filteredRecordArrays.get(typeClass); var filter; @@ -117,14 +117,14 @@ export default Ember.Object.extend({ if (!filter) { shouldBeInArray = true; } else { - shouldBeInArray = filter(record); + shouldBeInArray = filter(record.getRecord()); } var recordArrays = this.recordArraysForRecord(record); if (shouldBeInArray) { if (!recordArrays.has(array)) { - array._pushRecord(record); + array.addRecord(record); recordArrays.add(array); } } else if (!shouldBeInArray) { @@ -153,7 +153,7 @@ export default Ember.Object.extend({ for (var i = 0, l = records.length; i < l; i++) { record = records[i]; - if (!get(record, 'isDeleted') && !get(record, 'isEmpty')) { + if (!record.isDeleted() && !record.isEmpty()) { this.updateRecordArray(array, filter, modelName, record); } } diff --git a/packages/ember-data/lib/system/record-arrays/adapter-populated-record-array.js b/packages/ember-data/lib/system/record-arrays/adapter-populated-record-array.js index 1d7a24f00e7..65670d58b90 100644 --- a/packages/ember-data/lib/system/record-arrays/adapter-populated-record-array.js +++ b/packages/ember-data/lib/system/record-arrays/adapter-populated-record-array.js @@ -43,13 +43,15 @@ export default RecordArray.extend({ var records = store.pushMany(modelName, data); var meta = store.metadataFor(modelName); + //TODO Optimize + var internalModels = Ember.A(records).mapBy('_internalModel'); this.setProperties({ - content: Ember.A(records), + content: Ember.A(internalModels), isLoaded: true, meta: cloneNull(meta) }); - records.forEach(function(record) { + internalModels.forEach(function(record) { this.manager.recordArraysForRecord(record).add(this); }, this); diff --git a/packages/ember-data/lib/system/record-arrays/record-array.js b/packages/ember-data/lib/system/record-arrays/record-array.js index a1c98541f6d..180bf51ff2c 100644 --- a/packages/ember-data/lib/system/record-arrays/record-array.js +++ b/packages/ember-data/lib/system/record-arrays/record-array.js @@ -90,8 +90,8 @@ export default Ember.ArrayProxy.extend(Ember.Evented, { */ objectAtContent: function(index) { var content = get(this, 'content'); - - return content.objectAt(index); + var internalModel = content.objectAt(index); + return internalModel && internalModel.getRecord(); }, /** @@ -135,22 +135,6 @@ export default Ember.ArrayProxy.extend(Ember.Evented, { } }, - _pushRecord: function(record) { - get(this, 'content').pushObject(record); - }, - - /** - Adds a record to the `RecordArray`, but allows duplicates - - @deprecated - @method pushRecord - @private - @param {DS.Model} record - */ - pushRecord: function(record) { - Ember.deprecate('Usage of `recordArray.pushRecord` is deprecated, use `recordArray.addObject` instead'); - this._pushRecord(record); - }, /** Removes a record to the `RecordArray`. @@ -191,7 +175,7 @@ export default Ember.ArrayProxy.extend(Ember.Evented, { _dissociateFromOwnRecords: function() { var array = this; - this.forEach(function(record) { + this.get('content').forEach(function(record) { var recordArrays = record._recordArrays; if (recordArrays) { @@ -206,10 +190,7 @@ export default Ember.ArrayProxy.extend(Ember.Evented, { */ _unregisterFromManager: function() { var manager = get(this, 'manager'); - //We will stop needing this stupid if statement soon, once manyArray are refactored to not be RecordArrays - if (manager) { - manager.unregisterFilteredRecordArray(this); - } + manager.unregisterFilteredRecordArray(this); }, willDestroy: function() { diff --git a/packages/ember-data/lib/system/relationships/belongs-to.js b/packages/ember-data/lib/system/relationships/belongs-to.js index 85ec51d3837..63a0d14d786 100644 --- a/packages/ember-data/lib/system/relationships/belongs-to.js +++ b/packages/ember-data/lib/system/relationships/belongs-to.js @@ -89,19 +89,21 @@ function belongsTo(modelName, options) { return computedPolyfill({ get: function(key) { - return this._relationships[key].getRecord(); + return this._internalModel._relationships[key].getRecord(); }, set: function(key, value) { if (value === undefined) { value = null; } if (value && value.then) { - this._relationships[key].setRecordPromise(value); + this._internalModel._relationships[key].setRecordPromise(value); + } else if (value) { + this._internalModel._relationships[key].setRecord(value._internalModel); } else { - this._relationships[key].setRecord(value); + this._internalModel._relationships[key].setRecord(value); } - return this._relationships[key].getRecord(); + return this._internalModel._relationships[key].getRecord(); } }).meta(meta); } diff --git a/packages/ember-data/lib/system/relationships/has-many.js b/packages/ember-data/lib/system/relationships/has-many.js index e7baf49141c..06a5144d647 100644 --- a/packages/ember-data/lib/system/relationships/has-many.js +++ b/packages/ember-data/lib/system/relationships/has-many.js @@ -123,13 +123,14 @@ function hasMany(type, options) { return computedPolyfill({ get: function(key) { - var relationship = this._relationships[key]; + var relationship = this._internalModel._relationships[key]; return relationship.getRecords(); }, set: function(key, records) { - var relationship = this._relationships[key]; + var relationship = this._internalModel._relationships[key]; relationship.clear(); - relationship.addRecords(records); + Ember.assert("You must pass an array of records to set a hasMany relationship", Ember.isArray(records)); + relationship.addRecords(Ember.A(records).mapBy('_internalModel')); return relationship.getRecords(); } }).meta(meta); diff --git a/packages/ember-data/lib/system/relationships/state/belongs-to.js b/packages/ember-data/lib/system/relationships/state/belongs-to.js index b658ca20cdf..7409bd77925 100644 --- a/packages/ember-data/lib/system/relationships/state/belongs-to.js +++ b/packages/ember-data/lib/system/relationships/state/belongs-to.js @@ -50,7 +50,7 @@ BelongsToRelationship.prototype._super$flushCanonical = Relationship.prototype.f BelongsToRelationship.prototype.flushCanonical = function() { //temporary fix to not remove newly created records if server returned null. //TODO remove once we have proper diffing - if (this.inverseRecord && this.inverseRecord.get('isNew') && !this.canonicalState) { + if (this.inverseRecord && this.inverseRecord.isNew() && !this.canonicalState) { return; } this.inverseRecord = this.canonicalState; @@ -62,14 +62,16 @@ BelongsToRelationship.prototype._super$addRecord = Relationship.prototype.addRec BelongsToRelationship.prototype.addRecord = function(newRecord) { if (this.members.has(newRecord)) { return;} var typeClass = this.store.modelFor(this.relationshipMeta.type); - Ember.assert("You cannot add a '" + newRecord.constructor.modelName + "' record to the '" + this.record.constructor.modelName + "." + this.key +"'. " + "You can only add a '" + typeClass.modelName + "' record to this relationship.", (function () { + Ember.assert("You cannot add a '" + newRecord.type.modelName + "' record to the '" + this.record.type.modelName + "." + this.key +"'. " + "You can only add a '" + typeClass.modelName + "' record to this relationship.", (function () { if (typeClass.__isMixin) { - return typeClass.__mixin.detect(newRecord); + //TODO Need to do this in order to support mixins, should convert to public api + //once it exists in Ember + return typeClass.__mixin.detect(newRecord.type.PrototypeMixin); } if (Ember.MODEL_FACTORY_INJECTIONS) { typeClass = typeClass.superclass; } - return newRecord instanceof typeClass; + return typeClass.detect(newRecord.type); })()); if (this.inverseRecord) { @@ -104,7 +106,7 @@ BelongsToRelationship.prototype.removeCanonicalRecordFromOwn = function(record) BelongsToRelationship.prototype.findRecord = function() { if (this.inverseRecord) { - return this.store._findByRecord(this.inverseRecord); + return this.store._findByInternalModel(this.inverseRecord); } else { return Ember.RSVP.Promise.resolve(null); } @@ -138,8 +140,12 @@ BelongsToRelationship.prototype.getRecord = function() { content: this.inverseRecord }); } else { - Ember.assert("You looked up the '" + this.key + "' relationship on a '" + this.record.constructor.modelName + "' with id " + this.record.get('id') + " but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.belongsTo({ async: true })`)", this.inverseRecord === null || !this.inverseRecord.get('isEmpty')); - return this.inverseRecord; + if (this.inverseRecord === null) { + return null; + } + var toReturn = this.inverseRecord.getRecord(); + Ember.assert("You looked up the '" + this.key + "' relationship on a '" + this.record.type.modelName + "' with id " + this.record.id + " but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.belongsTo({ async: true })`)", toReturn === null || !toReturn.get('isEmpty')); + return toReturn; } }; diff --git a/packages/ember-data/lib/system/relationships/state/create.js b/packages/ember-data/lib/system/relationships/state/create.js index 778d5d44087..2519241ee44 100644 --- a/packages/ember-data/lib/system/relationships/state/create.js +++ b/packages/ember-data/lib/system/relationships/state/create.js @@ -3,7 +3,7 @@ import BelongsToRelationship from "ember-data/system/relationships/state/belongs var createRelationshipFor = function(record, relationshipMeta, store) { var inverseKey; - var inverse = record.constructor.inverseFor(relationshipMeta.key, store); + var inverse = record.type.inverseFor(relationshipMeta.key, store); if (inverse) { inverseKey = inverse.name; diff --git a/packages/ember-data/lib/system/relationships/state/has-many.js b/packages/ember-data/lib/system/relationships/state/has-many.js index 775f47a08a7..4e9e03f95ad 100644 --- a/packages/ember-data/lib/system/relationships/state/has-many.js +++ b/packages/ember-data/lib/system/relationships/state/has-many.js @@ -3,6 +3,8 @@ import Relationship from "ember-data/system/relationships/state/relationship"; import OrderedSet from "ember-data/system/ordered-set"; import ManyArray from "ember-data/system/many-array"; +var map = Ember.EnumerableUtils.map; + var ManyRelationship = function(store, record, inverseKey, relationshipMeta) { this._super$constructor(store, record, inverseKey, relationshipMeta); this.belongsToType = relationshipMeta.type; @@ -85,14 +87,14 @@ ManyRelationship.prototype.removeRecordFromOwn = function(record, idx) { ManyRelationship.prototype.notifyRecordRelationshipAdded = function(record, idx) { var typeClass = this.store.modelFor(this.relationshipMeta.type); - Ember.assert("You cannot add '" + record.constructor.modelName + "' records to the " + this.record.constructor.modelName + "." + this.key + " relationship (only '" + typeClass.modelName + "' allowed)", (function () { + Ember.assert("You cannot add '" + record.type.modelName + "' records to the " + this.record.type.modelName + "." + this.key + " relationship (only '" + typeClass.modelName + "' allowed)", (function () { if (typeClass.__isMixin) { - return typeClass.__mixin.detect(record); + return typeClass.__mixin.detect(record.type.PrototypeMixin); } if (Ember.MODEL_FACTORY_INJECTIONS) { typeClass = typeClass.superclass; } - return record instanceof typeClass; + return typeClass.detect(record.type); })()); this.record.notifyHasManyAdded(this.key, record, idx); @@ -153,7 +155,8 @@ ManyRelationship.prototype.fetchLink = function() { ManyRelationship.prototype.findRecords = function() { var manyArray = this.manyArray; - return this.store.findMany(manyArray.toArray()).then(function() { + //TODO CLEANUP + return this.store.findMany(map(manyArray.toArray(), function(rec) { return rec._internalModel; })).then(function() { //Goes away after the manyArray refactor manyArray.set('isLoaded', true); return manyArray; @@ -180,7 +183,7 @@ ManyRelationship.prototype.getRecords = function() { promise: promise }); } else { - Ember.assert("You looked up the '" + this.key + "' relationship on a '" + this.record.constructor.modelName + "' with id " + this.record.get('id') + " but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.hasMany({ async: true })`)", this.manyArray.isEvery('isEmpty', false)); + Ember.assert("You looked up the '" + this.key + "' relationship on a '" + this.record.type.modelName + "' with id " + this.record.id + " but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.hasMany({ async: true })`)", this.manyArray.isEvery('isEmpty', false)); //TODO(Igor) WTF DO I DO HERE? if (!this.manyArray.get('isDestroyed')) { diff --git a/packages/ember-data/lib/system/relationships/state/relationship.js b/packages/ember-data/lib/system/relationships/state/relationship.js index 4dffdee23f1..e587ef5e380 100644 --- a/packages/ember-data/lib/system/relationships/state/relationship.js +++ b/packages/ember-data/lib/system/relationships/state/relationship.js @@ -181,7 +181,7 @@ Relationship.prototype = { //TODO remove once we have proper diffing var newRecords = []; for (var i=0; i { author: 'Tomster', title: 'Ember.js rocks' } ``` @@ -146,6 +150,31 @@ Snapshot.prototype = { return Ember.copy(this._attributes); }, + /** + Returns all changed attributes and their old and new values. + + Example + + ```javascript + // store.push('post', { id: 1, author: 'Tomster', title: 'Ember.js rocks' }); + postModel.set('title', 'Ember.js rocks!'); + postSnapshot.changedAttributes(); // => { title: ['Ember.js rocks', 'Ember.js rocks!'] } + ``` + + @method changedAttributes + @return {Object} All changed attributes of the current snapshot + */ + changedAttributes: function() { + var prop; + var changedAttributes = Ember.create(null); + + for (prop in this._changedAttributes) { + changedAttributes[prop] = Ember.copy(this._changedAttributes[prop]); + } + + return changedAttributes; + }, + /** Returns the current value of a belongsTo relationship. @@ -194,7 +223,7 @@ Snapshot.prototype = { return this._belongsToRelationships[keyName]; } - relationship = this.record._relationships[keyName]; + relationship = this._internalModel._relationships[keyName]; if (!(relationship && relationship.relationshipMeta.kind === 'belongsTo')) { throw new Ember.Error("Model '" + Ember.inspect(this.record) + "' has no belongsTo relationship named '" + keyName + "' defined."); } @@ -207,7 +236,7 @@ Snapshot.prototype = { if (id) { result = get(inverseRecord, 'id'); } else { - result = inverseRecord._createSnapshot(); + result = inverseRecord.createSnapshot(); } } else { result = null; @@ -265,7 +294,7 @@ Snapshot.prototype = { return this._hasManyRelationships[keyName]; } - relationship = this.record._relationships[keyName]; + relationship = this._internalModel._relationships[keyName]; if (!(relationship && relationship.relationshipMeta.kind === 'hasMany')) { throw new Ember.Error("Model '" + Ember.inspect(this.record) + "' has no hasMany relationship named '" + keyName + "' defined."); } @@ -277,9 +306,9 @@ Snapshot.prototype = { results = []; members.forEach(function(member) { if (ids) { - results.push(get(member, 'id')); + results.push(member.id); } else { - results.push(member._createSnapshot()); + results.push(member.createSnapshot()); } }); } @@ -350,7 +379,7 @@ Snapshot.prototype = { return this.attr(keyName); } - var relationship = this.record._relationships[keyName]; + var relationship = this._internalModel._relationships[keyName]; if (relationship && relationship.relationshipMeta.kind === 'belongsTo') { return this.belongsTo(keyName); diff --git a/packages/ember-data/lib/system/store.js b/packages/ember-data/lib/system/store.js index 4777fc8d4f6..1f3d758e07b 100644 --- a/packages/ember-data/lib/system/store.js +++ b/packages/ember-data/lib/system/store.js @@ -38,8 +38,12 @@ import { _findQuery } from "ember-data/system/store/finders"; +import coerceId from "ember-data/system/coerce-id"; + import RecordArrayManager from "ember-data/system/record-array-manager"; +import InternalModel from "ember-data/system/model/internal-model"; + import Model from "ember-data/system/model"; var Backburner = Ember.Backburner || Ember.__loader.require('backburner')['default'] || Ember.__loader.require('backburner')['Backburner']; @@ -84,6 +88,20 @@ if (!Backburner.prototype.join) { } +//Get the materialized model from the internalModel/promise that returns +//an internal model and return it in a promiseObject. Useful for returning +//from find methods +function promiseRecord(internalModel, label) { + //TODO cleanup + var toReturn = internalModel; + if (!internalModel.then) { + toReturn = internalModel.getRecord(); + } else { + toReturn = internalModel.then((model) => model.getRecord()); + } + return promiseObject(toReturn, label); +} + var get = Ember.get; var set = Ember.set; var once = Ember.run.once; @@ -111,20 +129,10 @@ if (!Service) { // * +clientId+ means a transient numerical identifier generated at runtime by // the data store. It is important primarily because newly created objects may // not yet have an externally generated id. -// * +reference+ means a record reference object, which holds metadata about a +// * +internalModel+ means a record internalModel object, which holds metadata about a // record, even if it has not yet been fully materialized. // * +type+ means a subclass of DS.Model. -// Used by the store to normalize IDs entering the store. Despite the fact -// that developers may provide IDs as numbers (e.g., `store.find(Person, 1)`), -// it is important that internally we use strings, since IDs may be serialized -// and lose type information. For example, Ember's router may put a record's -// ID into the URL, and if we later try to deserialize that URL and find the -// corresponding record, we will not know if it is a string or a number. -function coerceId(id) { - return id == null ? null : id+''; -} - /** The store contains all of the data for records loaded from the server. It is also responsible for creating instances of `DS.Model` that wrap @@ -243,7 +251,7 @@ Store = Service.extend({ @param {Object} options an options hash */ serialize: function(record, options) { - var snapshot = record._createSnapshot(); + var snapshot = record._internalModel.createSnapshot(); return this.serializerFor(snapshot.modelName).serialize(snapshot, options); }, @@ -306,7 +314,7 @@ Store = Service.extend({ createRecord: function(modelName, inputProperties) { Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); var typeClass = this.modelFor(modelName); - var properties = copy(inputProperties) || {}; + var properties = copy(inputProperties) || Ember.create(null); // If the passed properties do not include a primary key, // give the adapter an opportunity to generate one. Typically, @@ -320,17 +328,18 @@ Store = Service.extend({ // Coerce ID to a string properties.id = coerceId(properties.id); - var record = this.buildRecord(typeClass, properties.id); + var internalModel = this.buildInternalModel(typeClass, properties.id); + var record = internalModel.getRecord(); // Move the record out of its initial `empty` state into // the `loaded` state. - record.loadedData(); + internalModel.loadedData(); // Set the properties specified on the record. record.setProperties(properties); - record.eachRelationship(function(key, descriptor) { - record._relationships[key].setHasData(true); + internalModel.eachRelationship(function(key, descriptor) { + internalModel._relationships[key].setHasData(true); }); return record; @@ -601,29 +610,27 @@ Store = Service.extend({ */ findById: function(modelName, id, preload) { Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); + var internalModel = this._internalModelForId(modelName, id); - var record = this.recordForId(modelName, id); - - return this._findByRecord(record, preload); + return this._findByInternalModel(internalModel, preload); }, - _findByRecord: function(record, preload) { - var fetchedRecord; + _findByInternalModel: function(internalModel, preload) { + var fetchedInternalModel; if (preload) { - record._preloadData(preload); + internalModel._preloadData(preload); } - if (get(record, 'isEmpty')) { - fetchedRecord = this.scheduleFetch(record); + if (internalModel.isEmpty()) { + fetchedInternalModel = this.scheduleFetch(internalModel); //TODO double check about reloading - } else if (get(record, 'isLoading')) { - fetchedRecord = record._loadingPromise; + } else if (internalModel.isLoading()) { + fetchedInternalModel = internalModel._loadingPromise; } - return promiseObject(fetchedRecord || record, "DS: Store#findByRecord " + record.modelName + " with id: " + get(record, 'id')); + return promiseRecord(fetchedInternalModel || internalModel, "DS: Store#findByRecord " + internalModel.typeKey + " with id: " + get(internalModel, 'id')); }, - /** This method makes a series of requests to the adapter's `find` method and returns a promise that resolves once they are all loaded. @@ -650,38 +657,39 @@ Store = Service.extend({ @method fetchRecord @private - @param {DS.Model} record + @param {InternalModel} internal model @return {Promise} promise */ - fetchRecord: function(record) { - var typeClass = record.constructor; - var id = get(record, 'id'); + fetchRecord: function(internalModel) { + var typeClass = internalModel.type; + var id = internalModel.id; var adapter = this.adapterFor(typeClass.modelName); Ember.assert("You tried to find a record but you have no adapter (for " + typeClass + ")", adapter); Ember.assert("You tried to find a record but your adapter (for " + typeClass + ") does not implement 'find'", typeof adapter.find === 'function'); - var promise = _find(adapter, this, typeClass, id, record); + var promise = _find(adapter, this, typeClass, id, internalModel); return promise; }, scheduleFetchMany: function(records) { - return Promise.all(map(records, this.scheduleFetch, this)); + var internalModels = map(records, function(record) { return record._internalModel; }); + return Promise.all(map(internalModels, this.scheduleFetch, this)); }, - scheduleFetch: function(record) { - var typeClass = record.constructor; - if (isNone(record)) { return null; } - if (record._loadingPromise) { return record._loadingPromise; } + scheduleFetch: function(internalModel) { + var typeClass = internalModel.type; - var resolver = Ember.RSVP.defer('Fetching ' + typeClass + 'with id: ' + record.get('id')); + if (internalModel._loadingPromise) { return internalModel._loadingPromise; } + + var resolver = Ember.RSVP.defer('Fetching ' + typeClass + 'with id: ' + internalModel.id); var recordResolverPair = { - record: record, + record: internalModel, resolver: resolver }; var promise = resolver.promise; - record.loadingData(promise); + internalModel.loadingData(promise); if (!this._pendingFetch.get(typeClass)) { this._pendingFetch.set(typeClass, [recordResolverPair]); @@ -767,10 +775,10 @@ Store = Service.extend({ // records from the grouped snapshots even though the _findMany() finder // will once again convert the records to snapshots for adapter.findMany() - var snapshots = Ember.A(records).invoke('_createSnapshot'); + var snapshots = Ember.A(records).invoke('createSnapshot'); var groups = adapter.groupRecordsForFindMany(this, snapshots); forEach(groups, function (groupOfSnapshots) { - var groupOfRecords = Ember.A(groupOfSnapshots).mapBy('record'); + var groupOfRecords = Ember.A(groupOfSnapshots).mapBy('_internalModel'); var requestedRecords = Ember.A(groupOfRecords); var ids = requestedRecords.mapBy('id'); if (ids.length > 1) { @@ -813,7 +821,7 @@ Store = Service.extend({ getById: function(modelName, id) { Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); if (this.hasRecordForId(modelName, id)) { - return this.recordForId(modelName, id); + return this._internalModelForId(modelName, id).getRecord(); } else { return null; } @@ -831,16 +839,16 @@ Store = Service.extend({ @param {DS.Model} record @return {Promise} promise */ - reloadRecord: function(record) { - var type = record.constructor; - var adapter = this.adapterFor(type.modelName); - var id = get(record, 'id'); + reloadRecord: function(internalModel) { + var modelName = internalModel.type.modelName; + var adapter = this.adapterFor(modelName); + var id = internalModel.id; Ember.assert("You cannot reload a record without an ID", id); - Ember.assert("You tried to reload a record but you have no adapter (for " + type + ")", adapter); + Ember.assert("You tried to reload a record but you have no adapter (for " + modelName + ")", adapter); Ember.assert("You tried to reload a record but your adapter does not implement `find`", typeof adapter.find === 'function'); - return this.scheduleFetch(record); + return this.scheduleFetch(internalModel); }, /** @@ -855,8 +863,8 @@ Store = Service.extend({ Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); var typeClass = this.modelFor(modelName); var id = coerceId(inputId); - var record = this.typeMapFor(typeClass).idToRecord[id]; - return !!record && get(record, 'isLoaded'); + var internalModel = this.typeMapFor(typeClass).idToRecord[id]; + return !!internalModel && internalModel.isLoaded(); }, /** @@ -869,33 +877,36 @@ Store = Service.extend({ @param {String|Integer} id @return {DS.Model} record */ - recordForId: function(modelName, inputId) { + recordForId: function(modelName, id) { Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); - var typeClass = this.modelFor(modelName); + return this._internalModelForId(modelName, id).getRecord(); + }, + + _internalModelForId: function(typeName, inputId) { + var typeClass = this.modelFor(typeName); var id = coerceId(inputId); var idToRecord = this.typeMapFor(typeClass).idToRecord; var record = idToRecord[id]; if (!record || !idToRecord[id]) { - record = this.buildRecord(typeClass, id); + record = this.buildInternalModel(typeClass, id); } return record; }, + + /** @method findMany @private - @param {DS.Model} owner - @param {Array} records - @param {String or subclass of DS.Model} type - @param {Resolver} resolver + @param {Array} internalModels @return {Promise} promise */ - findMany: function(records) { + findMany: function(internalModels) { var store = this; - return Promise.all(map(records, function(record) { - return store._findByRecord(record); + return Promise.all(map(internalModels, function(internalModel) { + return store._findByInternalModel(internalModel); })); }, @@ -919,9 +930,9 @@ Store = Service.extend({ @return {Promise} promise */ findHasMany: function(owner, link, type) { - var adapter = this.adapterFor(owner.constructor.modelName); + var adapter = this.adapterFor(owner.type.modelName); - Ember.assert("You tried to load a hasMany relationship but you have no adapter (for " + owner.constructor + ")", adapter); + Ember.assert("You tried to load a hasMany relationship but you have no adapter (for " + owner.type + ")", adapter); Ember.assert("You tried to load a hasMany relationship from a specified `link` in the original payload but your adapter does not implement `findHasMany`", typeof adapter.findHasMany === 'function'); return _findHasMany(adapter, this, owner, link, type); @@ -936,9 +947,9 @@ Store = Service.extend({ @return {Promise} promise */ findBelongsTo: function(owner, link, relationship) { - var adapter = this.adapterFor(owner.constructor.modelName); + var adapter = this.adapterFor(owner.type.modelName); - Ember.assert("You tried to load a belongsTo relationship but you have no adapter (for " + owner.constructor + ")", adapter); + Ember.assert("You tried to load a belongsTo relationship but you have no adapter (for " + owner.type + ")", adapter); Ember.assert("You tried to load a belongsTo relationship from a specified `link` in the original payload but your adapter does not implement `findBelongsTo`", typeof adapter.findBelongsTo === 'function'); return _findBelongsTo(adapter, this, owner, link, relationship); @@ -1204,8 +1215,7 @@ Store = Service.extend({ */ recordIsLoaded: function(modelName, id) { Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); - if (!this.hasRecordForId(modelName, id)) { return false; } - return !get(this.recordForId(modelName, id), 'isEmpty'); + return this.hasRecordForId(modelName, id); }, /** @@ -1248,10 +1258,10 @@ Store = Service.extend({ @method dataWasUpdated @private @param {Class} type - @param {DS.Model} record + @param {InternalModel} internal model */ - dataWasUpdated: function(type, record) { - this.recordArrayManager.recordDidChange(record); + dataWasUpdated: function(type, internalModel) { + this.recordArrayManager.recordDidChange(internalModel); }, // .............. @@ -1266,12 +1276,14 @@ Store = Service.extend({ @method scheduleSave @private - @param {DS.Model} record + @param {InternalModel} internal model @param {Resolver} resolver */ - scheduleSave: function(record, resolver) { - record.adapterWillCommit(); - this._pendingSave.push([record, resolver]); + scheduleSave: function(internalModel, resolver) { + var snapshot = internalModel.createSnapshot(); + internalModel.flushChangedAttributes(); + internalModel.adapterWillCommit(); + this._pendingSave.push([snapshot, resolver]); once(this, 'flushPendingSave'); }, @@ -1287,22 +1299,23 @@ Store = Service.extend({ this._pendingSave = []; forEach(pending, function(tuple) { - var record = tuple[0]; + var snapshot = tuple[0]; var resolver = tuple[1]; - var adapter = this.adapterFor(record.constructor.modelName); + var record = snapshot._internalModel; + var adapter = this.adapterFor(record.type.modelName); var operation; if (get(record, 'currentState.stateName') === 'root.deleted.saved') { - return resolver.resolve(record); - } else if (get(record, 'isNew')) { + return resolver.resolve(); + } else if (record.isNew()) { operation = 'createRecord'; - } else if (get(record, 'isDeleted')) { + } else if (record.isDeleted()) { operation = 'deleteRecord'; } else { operation = 'updateRecord'; } - resolver.resolve(_commit(adapter, this, operation, record)); + resolver.resolve(_commit(adapter, this, operation, snapshot)); }, this); }, @@ -1316,19 +1329,19 @@ Store = Service.extend({ @method didSaveRecord @private - @param {DS.Model} record the in-flight record + @param {InternalModel} internal model the in-flight internal model @param {Object} data optional data (see above) */ - didSaveRecord: function(record, data) { + didSaveRecord: function(internalModel, data) { if (data) { // normalize relationship IDs into records - this._backburner.schedule('normalizeRelationships', this, '_setupRelationships', record, record.constructor, data); - this.updateId(record, data); + this._backburner.schedule('normalizeRelationships', this, '_setupRelationships', internalModel, internalModel.type, data); + this.updateId(internalModel, data); } //We first make sure the primary data has been updated //TODO try to move notification to the user to the end of the runloop - record.adapterDidCommit(data); + internalModel.adapterDidCommit(data); }, /** @@ -1338,11 +1351,11 @@ Store = Service.extend({ @method recordWasInvalid @private - @param {DS.Model} record + @param {InternalModel} internal model @param {Object} errors */ - recordWasInvalid: function(record, errors) { - record.adapterDidInvalidate(errors); + recordWasInvalid: function(internalModel, errors) { + internalModel.adapterDidInvalidate(errors); }, /** @@ -1352,10 +1365,10 @@ Store = Service.extend({ @method recordWasError @private - @param {DS.Model} record + @param {InternalModel} internal model */ - recordWasError: function(record) { - record.adapterDidError(); + recordWasError: function(internalModel) { + internalModel.adapterDidError(); }, /** @@ -1365,18 +1378,18 @@ Store = Service.extend({ @method updateId @private - @param {DS.Model} record + @param {InternalModel} internal model @param {Object} data */ - updateId: function(record, data) { - var oldId = get(record, 'id'); + updateId: function(internalModel, data) { + var oldId = internalModel.id; var id = coerceId(data.id); - Ember.assert("An adapter cannot assign a new id to a record that already has an id. " + record + " had id: " + oldId + " and you tried to update it with " + id + ". This likely happened because your server returned data in response to a find or update that had a different id than the one you sent.", oldId === null || id === oldId); + Ember.assert("An adapter cannot assign a new id to a record that already has an id. " + internalModel + " had id: " + oldId + " and you tried to update it with " + id + ". This likely happened because your server returned data in response to a find or update that had a different id than the one you sent.", oldId === null || id === oldId); - this.typeMapFor(record.constructor).idToRecord[id] = record; + this.typeMapFor(internalModel.type).idToRecord[id] = internalModel; - set(record, 'id', id); + internalModel.setId(id); }, /** @@ -1420,12 +1433,13 @@ Store = Service.extend({ */ _load: function(type, data) { var id = coerceId(data.id); - var record = this.recordForId(type, id); + var internalModel = this._internalModelForId(type, id); - record.setupData(data); - this.recordArrayManager.recordDidChange(record); + internalModel.setupData(data); - return record; + this.recordArrayManager.recordDidChange(internalModel); + + return internalModel; }, /* @@ -1490,7 +1504,11 @@ Store = Service.extend({ configurable: false, get: function() { Ember.deprecate('Usage of `typeKey` has been deprecated and will be removed in Ember Data 1.0. It has been replaced by `modelName` on the model class.'); - return Ember.String.camelize(this.modelName); + var typeKey = this.modelName; + if (typeKey) { + typeKey = Ember.String.camelize(this.modelName); + } + return typeKey; }, set: function() { Ember.assert('Setting typeKey is not supported. In addition, typeKey has also been deprecated in favor of modelName. Setting modelName is also not supported.'); @@ -1576,6 +1594,11 @@ Store = Service.extend({ */ push: function(modelName, data) { Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string'); + var internalModel = this._pushInternalModel(modelName, data); + return internalModel.getRecord(); + }, + + _pushInternalModel: function(modelName, data) { Ember.assert("Expected an object as `data` in a call to `push` for " + modelName + " , but was " + data, Ember.typeOf(data) === 'object'); Ember.assert("You must include an `id` for " + modelName + " in an object passed to `push`", data.id != null && data.id !== ''); @@ -1596,17 +1619,15 @@ Store = Service.extend({ } // Actually load the record into the store. + var internalModel = this._load(modelName, data); - this._load(modelName, data); - - var record = this.recordForId(modelName, data.id); var store = this; this._backburner.join(function() { - store._backburner.schedule('normalizeRelationships', store, '_setupRelationships', record, type, data); + store._backburner.schedule('normalizeRelationships', store, '_setupRelationships', internalModel, type, data); }); - return record; + return internalModel; }, _setupRelationships: function(record, type, data) { @@ -1768,9 +1789,9 @@ Store = Service.extend({ @param {subclass of DS.Model} type @param {String} id @param {Object} data - @return {DS.Model} record + @return {InternalModel} internal model */ - buildRecord: function(type, id, data) { + buildInternalModel: function(type, id, data) { var typeMap = this.typeMapFor(type); var idToRecord = typeMap.idToRecord; @@ -1779,25 +1800,17 @@ Store = Service.extend({ // lookupFactory should really return an object that creates // instances with the injections applied - var record = type._create({ - id: id, - store: this, - container: this.container - }); - - if (data) { - record.setupData(data); - } + var internalModel = new InternalModel(type, id, this, this.container, data); // if we're creating an item, this process will be done // later, once the object has been persisted. if (id) { - idToRecord[id] = record; + idToRecord[id] = internalModel; } - typeMap.records.push(record); + typeMap.records.push(internalModel); - return record; + return internalModel; }, //Called by the state machine to notify the store that the record is ready to be interacted with @@ -1826,20 +1839,20 @@ Store = Service.extend({ @method _dematerializeRecord @private - @param {DS.Model} record + @param {InternalModel} internal model */ - _dematerializeRecord: function(record) { - var type = record.constructor; + _dematerializeRecord: function(internalModel) { + var type = internalModel.type; var typeMap = this.typeMapFor(type); - var id = get(record, 'id'); + var id = internalModel.id; - record.updateRecordArrays(); + internalModel.updateRecordArrays(); if (id) { delete typeMap.idToRecord[id]; } - var loc = indexOf(typeMap.records, record); + var loc = indexOf(typeMap.records, internalModel); typeMap.records.splice(loc, 1); }, @@ -2002,20 +2015,27 @@ function normalizeRelationships(store, type, data, record) { } function deserializeRecordId(store, data, key, relationship, id) { - if (isNone(id) || id instanceof Model) { + if (isNone(id)) { return; } + + //If record objects were given to push directly, uncommon, not sure whether we should actually support + if (id instanceof Model) { + data[key] = id._internalModel; + return; + } + Ember.assert("A " + relationship.parentType + " record was pushed into the store with the value of " + key + " being " + Ember.inspect(id) + ", but " + key + " is a belongsTo relationship so the value must not be an array. You should probably check your data payload or serializer.", !Ember.isArray(id)); var type; if (typeof id === 'number' || typeof id === 'string') { type = typeFor(relationship, key, data); - data[key] = store.recordForId(typeof type === 'string' ? type : type.modelName, id); + data[key] = store._internalModelForId(type, id); } else if (typeof id === 'object') { // hasMany polymorphic Ember.assert('Ember Data expected a number or string to represent the record(s) in the `' + relationship.key + '` relationship instead it found an object. If this is a polymorphic relationship please specify a `type` key. If this is an embedded relationship please include the `DS.EmbeddedRecordsMixin` and specify the `' + relationship.key +'` property in your serializer\'s attrs object.', id.type); - data[key] = store.recordForId(id.type, id.id); + data[key] = store._internalModelForId(id.type, id.id); } } @@ -2047,10 +2067,10 @@ function defaultSerializer(container) { container.lookup('serializer:-default'); } -function _commit(adapter, store, operation, record) { - var type = record.constructor; - var modelName = type.modelName; - var snapshot = record._createSnapshot(); +function _commit(adapter, store, operation, snapshot) { + var record = snapshot._internalModel; + var modelName = snapshot.modelName; + var type = store.modelFor(modelName); var promise = adapter[operation](store, type, snapshot); var serializer = serializerForAdapter(store, adapter, modelName); var label = "DS: Extract and notify about " + operation + " completion of " + record; @@ -2066,7 +2086,7 @@ function _commit(adapter, store, operation, record) { store._adapterRun(function() { if (adapterPayload) { - payload = serializer.extract(store, type, adapterPayload, get(record, 'id'), operation); + payload = serializer.extract(store, type, adapterPayload, snapshot.id, operation); } store.didSaveRecord(record, payload); }); @@ -2074,7 +2094,7 @@ function _commit(adapter, store, operation, record) { return record; }, function(reason) { if (reason instanceof InvalidError) { - var errors = serializer.extractErrors(store, type, reason.errors, get(record, 'id')); + var errors = serializer.extractErrors(store, type, reason.errors, snapshot.id); store.recordWasInvalid(record, errors); reason = new InvalidError(errors); } else { @@ -2086,7 +2106,7 @@ function _commit(adapter, store, operation, record) { } function setupRelationships(store, record, data) { - var typeClass = record.constructor; + var typeClass = record.type; typeClass.eachRelationship(function(key, descriptor) { var kind = descriptor.kind; diff --git a/packages/ember-data/lib/system/store/finders.js b/packages/ember-data/lib/system/store/finders.js index 0296ef02fc1..dbb6be5fbb6 100644 --- a/packages/ember-data/lib/system/store/finders.js +++ b/packages/ember-data/lib/system/store/finders.js @@ -9,14 +9,13 @@ import { } from "ember-data/system/store/serializers"; -var get = Ember.get; var Promise = Ember.RSVP.Promise; +var map = Ember.EnumerableUtils.map; -export function _find(adapter, store, typeClass, id, record) { - var modelName = typeClass.modelName; - var snapshot = record._createSnapshot(); +export function _find(adapter, store, typeClass, id, internalModel) { + var snapshot = internalModel.createSnapshot(); var promise = adapter.find(store, typeClass, id, snapshot); - var serializer = serializerForAdapter(store, adapter, modelName); + var serializer = serializerForAdapter(store, adapter, internalModel.type.modelName); var label = "DS: Handle Adapter#find of " + typeClass + " with id: " + id; promise = Promise.cast(promise, label); @@ -27,12 +26,14 @@ export function _find(adapter, store, typeClass, id, record) { return store._adapterRun(function() { var payload = serializer.extract(store, typeClass, adapterPayload, id, 'find'); - return store.push(modelName, payload); + //TODO Optimize + var record = store.push(typeClass.modelName, payload); + return record._internalModel; }); }, function(error) { - record.notFound(); - if (get(record, 'isEmpty')) { - store.unloadRecord(record); + internalModel.notFound(); + if (internalModel.isEmpty()) { + internalModel.unloadRecord(); } throw error; @@ -40,11 +41,10 @@ export function _find(adapter, store, typeClass, id, record) { } -export function _findMany(adapter, store, typeClass, ids, records) { - var modelName = typeClass.modelName; - var snapshots = Ember.A(records).invoke('_createSnapshot'); +export function _findMany(adapter, store, typeClass, ids, internalModels) { + var snapshots = Ember.A(internalModels).invoke('createSnapshot'); var promise = adapter.findMany(store, typeClass, ids, snapshots); - var serializer = serializerForAdapter(store, adapter, modelName); + var serializer = serializerForAdapter(store, adapter, typeClass.modelName); var label = "DS: Handle Adapter#findMany of " + typeClass; if (promise === undefined) { @@ -60,22 +60,23 @@ export function _findMany(adapter, store, typeClass, ids, records) { Ember.assert("The response from a findMany must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array'); - return store.pushMany(modelName, payload); + //TODO Optimize, no need to materialize here + var records = store.pushMany(typeClass.modelName, payload); + return map(records, function(record) { return record._internalModel; }); }); }, null, "DS: Extract payload of " + typeClass); } -export function _findHasMany(adapter, store, record, link, relationship) { - var snapshot = record._createSnapshot(); - var modelName = relationship.type; - var typeClass = store.modelFor(modelName); +export function _findHasMany(adapter, store, internalModel, link, relationship) { + var snapshot = internalModel.createSnapshot(); + var typeClass = store.modelFor(relationship.type); var promise = adapter.findHasMany(store, snapshot, link, relationship); - var serializer = serializerForAdapter(store, adapter, modelName); - var label = "DS: Handle Adapter#findHasMany of " + record + " : " + relationship.type; + var serializer = serializerForAdapter(store, adapter, relationship.type); + var label = "DS: Handle Adapter#findHasMany of " + internalModel + " : " + relationship.type; promise = Promise.cast(promise, label); promise = _guard(promise, _bind(_objectIsAlive, store)); - promise = _guard(promise, _bind(_objectIsAlive, record)); + promise = _guard(promise, _bind(_objectIsAlive, internalModel)); return promise.then(function(adapterPayload) { return store._adapterRun(function() { @@ -83,23 +84,23 @@ export function _findHasMany(adapter, store, record, link, relationship) { Ember.assert("The response from a findHasMany must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array'); - var records = store.pushMany(modelName, payload); - return records; + //TODO Use a non record creating push + var records = store.pushMany(relationship.type, payload); + return map(records, function(record) { return record._internalModel; }); }); - }, null, "DS: Extract payload of " + record + " : hasMany " + relationship.type); + }, null, "DS: Extract payload of " + internalModel + " : hasMany " + relationship.type); } -export function _findBelongsTo(adapter, store, record, link, relationship) { - var modelName = relationship.type; - var typeClass = store.modelFor(modelName); - var snapshot = record._createSnapshot(); +export function _findBelongsTo(adapter, store, internalModel, link, relationship) { + var snapshot = internalModel.createSnapshot(); + var typeClass = store.modelFor(relationship.type); var promise = adapter.findBelongsTo(store, snapshot, link, relationship); - var serializer = serializerForAdapter(store, adapter, modelName); - var label = "DS: Handle Adapter#findBelongsTo of " + record + " : " + relationship.type; + var serializer = serializerForAdapter(store, adapter, relationship.type); + var label = "DS: Handle Adapter#findBelongsTo of " + internalModel + " : " + relationship.type; promise = Promise.cast(promise, label); promise = _guard(promise, _bind(_objectIsAlive, store)); - promise = _guard(promise, _bind(_objectIsAlive, record)); + promise = _guard(promise, _bind(_objectIsAlive, internalModel)); return promise.then(function(adapterPayload) { return store._adapterRun(function() { @@ -109,10 +110,11 @@ export function _findBelongsTo(adapter, store, record, link, relationship) { return null; } - var record = store.push(modelName, payload); - return record; + var record = store.push(relationship.type, payload); + //TODO Optimize + return record._internalModel; }); - }, null, "DS: Extract payload of " + record + " : " + relationship.type); + }, null, "DS: Extract payload of " + internalModel + " : " + relationship.type); } export function _findAll(adapter, store, typeClass, sinceToken) { diff --git a/packages/ember-data/tests/integration/adapter/find-all-test.js b/packages/ember-data/tests/integration/adapter/find-all-test.js index a0541631765..50854511e44 100644 --- a/packages/ember-data/tests/integration/adapter/find-all-test.js +++ b/packages/ember-data/tests/integration/adapter/find-all-test.js @@ -91,6 +91,10 @@ test("When all records for a type are requested, a rejection should reject the p test("When all records for a type are requested, records that are already loaded should be returned immediately.", function() { expect(3); + store = createStore({ + adapter: DS.Adapter.extend(), + person: Person + }); run(function() { // Load a record from the server diff --git a/packages/ember-data/tests/integration/adapter/rest-adapter-test.js b/packages/ember-data/tests/integration/adapter/rest-adapter-test.js index 7098801ca74..5b2e2218b02 100644 --- a/packages/ember-data/tests/integration/adapter/rest-adapter-test.js +++ b/packages/ember-data/tests/integration/adapter/rest-adapter-test.js @@ -486,7 +486,7 @@ test("create - response can contain relationships the client doesn't yet know ab var postRecords = store.typeMapFor(Post).records; for (var i = 0; i < postRecords.length; i++) { - equal(post, postRecords[i], "The object in the identity map is the same"); + equal(post, postRecords[i].getRecord(), "The object in the identity map is the same"); } })); }); diff --git a/packages/ember-data/tests/integration/record-array-manager-test.js b/packages/ember-data/tests/integration/record-array-manager-test.js index fbef834d1b8..f059fff3787 100644 --- a/packages/ember-data/tests/integration/record-array-manager-test.js +++ b/packages/ember-data/tests/integration/record-array-manager-test.js @@ -84,17 +84,17 @@ test("destroying the store correctly cleans everything up", function() { equal(filterd2Summary.called.length, 0); - equal(person._recordArrays.list.length, 2, 'expected the person to be a member of 2 recordArrays'); + equal(person._internalModel._recordArrays.list.length, 2, 'expected the person to be a member of 2 recordArrays'); Ember.run(filterd2, filterd2.destroy); - equal(person._recordArrays.list.length, 1, 'expected the person to be a member of 1 recordArrays'); + equal(person._internalModel._recordArrays.list.length, 1, 'expected the person to be a member of 1 recordArrays'); equal(filterd2Summary.called.length, 1); Ember.run(manager, manager.destroy); - equal(person._recordArrays.list.length, 0, 'expected the person to be a member of no recordArrays'); + equal(person._internalModel._recordArrays.list.length, 0, 'expected the person to be a member of no recordArrays'); equal(filterd2Summary.called.length, 1); diff --git a/packages/ember-data/tests/integration/records/save-test.js b/packages/ember-data/tests/integration/records/save-test.js index cc432f3169d..10fc17fbd50 100644 --- a/packages/ember-data/tests/integration/records/save-test.js +++ b/packages/ember-data/tests/integration/records/save-test.js @@ -18,19 +18,28 @@ module("integration/records/save - Save Record", { }); test("Will resolve save on success", function() { - expect(1); + expect(4); var post; run(function() { post = env.store.createRecord('post', { title: 'toto' }); }); + var deferred = Ember.RSVP.defer(); env.adapter.createRecord = function(store, type, snapshot) { - return Ember.RSVP.resolve({ id: 123 }); + return deferred.promise; }; run(function() { - post.save().then(function() { + var saved = post.save(); + + // `save` returns a PromiseObject which allows to call get on it + equal(saved.get('id'), undefined); + + deferred.resolve({ id: 123 }); + saved.then(function(model) { ok(true, 'save operation was resolved'); + equal(saved.get('id'), 123); + equal(model, post, "resolves with the model"); }); }); }); diff --git a/packages/ember-data/tests/integration/relationships/belongs-to-test.js b/packages/ember-data/tests/integration/relationships/belongs-to-test.js index db4ba5d33e7..5c3a5399989 100644 --- a/packages/ember-data/tests/integration/relationships/belongs-to-test.js +++ b/packages/ember-data/tests/integration/relationships/belongs-to-test.js @@ -609,7 +609,7 @@ test("belongsTo hasData async loaded", function () { run(function() { store.find('book', 1).then(function(book) { - var relationship = book._relationships['author']; + var relationship = book._internalModel._relationships['author']; equal(relationship.hasData, true, 'relationship has data'); }); }); @@ -624,7 +624,7 @@ test("belongsTo hasData sync loaded", function () { run(function() { store.find('book', 1).then(function(book) { - var relationship = book._relationships['author']; + var relationship = book._internalModel._relationships['author']; equal(relationship.hasData, true, 'relationship has data'); }); }); @@ -643,7 +643,7 @@ test("belongsTo hasData async not loaded", function () { run(function() { store.find('book', 1).then(function(book) { - var relationship = book._relationships['author']; + var relationship = book._internalModel._relationships['author']; equal(relationship.hasData, false, 'relationship does not have data'); }); }); @@ -658,7 +658,7 @@ test("belongsTo hasData sync not loaded", function () { run(function() { store.find('book', 1).then(function(book) { - var relationship = book._relationships['author']; + var relationship = book._internalModel._relationships['author']; equal(relationship.hasData, false, 'relationship does not have data'); }); }); @@ -673,7 +673,7 @@ test("belongsTo hasData async created", function () { run(function() { var book = store.createRecord('book', { name: 'The Greatest Book' }); - var relationship = book._relationships['author']; + var relationship = book._internalModel._relationships['author']; equal(relationship.hasData, true, 'relationship has data'); }); }); @@ -683,7 +683,7 @@ test("belongsTo hasData sync created", function () { run(function() { var book = store.createRecord('book', { name: 'The Greatest Book' }); - var relationship = book._relationships['author']; + var relationship = book._internalModel._relationships['author']; equal(relationship.hasData, true, 'relationship has data'); }); }); diff --git a/packages/ember-data/tests/integration/relationships/has-many-test.js b/packages/ember-data/tests/integration/relationships/has-many-test.js index 42584542cd7..780fa5feadd 100644 --- a/packages/ember-data/tests/integration/relationships/has-many-test.js +++ b/packages/ember-data/tests/integration/relationships/has-many-test.js @@ -127,7 +127,7 @@ test("adapter.findMany only gets unique IDs even if duplicate IDs are present in }); // This tests the case where a serializer materializes a has-many -// relationship as a reference that it can fetch lazily. The most +// relationship as a internalModel that it can fetch lazily. The most // common use case of this is to provide a URL to a collection that // is loaded later. test("A serializer can materialize a hasMany as an opaque token that can be lazily fetched via the adapter's findHasMany hook", function() { @@ -970,7 +970,7 @@ test("dual non-async HM <-> BT", function() { deepEqual(post, commentPost, 'expect the new comments post, to be the correct post'); ok(postComments, "comments should exist"); - equal(postCommentsLength, 2, "comment's post should have a reference back to comment"); + equal(postCommentsLength, 2, "comment's post should have a internalModel back to comment"); ok(postComments && postComments.indexOf(firstComment) !== -1, 'expect to contain first comment'); ok(postComments && postComments.indexOf(comment) !== -1, 'expected to contain the new comment'); }); @@ -1218,7 +1218,7 @@ test("Relationship.clear removes all records correctly", function() { }); run(function() { - post._relationships['comments'].clear(); + post._internalModel._relationships['comments'].clear(); var comments = Ember.A(env.store.all('comment')); deepEqual(comments.mapBy('post'), [null, null, null]); }); @@ -1350,7 +1350,7 @@ test("hasMany hasData async loaded", function () { run(function() { store.find('chapter', 1).then(function(chapter) { - var relationship = chapter._relationships['pages']; + var relationship = chapter._internalModel._relationships['pages']; equal(relationship.hasData, true, 'relationship has data'); }); }); @@ -1365,7 +1365,7 @@ test("hasMany hasData sync loaded", function () { run(function() { store.find('chapter', 1).then(function(chapter) { - var relationship = chapter._relationships['pages']; + var relationship = chapter._internalModel._relationships['pages']; equal(relationship.hasData, true, 'relationship has data'); }); }); @@ -1384,7 +1384,7 @@ test("hasMany hasData async not loaded", function () { run(function() { store.find('chapter', 1).then(function(chapter) { - var relationship = chapter._relationships['pages']; + var relationship = chapter._internalModel._relationships['pages']; equal(relationship.hasData, false, 'relationship does not have data'); }); }); @@ -1399,7 +1399,7 @@ test("hasMany hasData sync not loaded", function () { run(function() { store.find('chapter', 1).then(function(chapter) { - var relationship = chapter._relationships['pages']; + var relationship = chapter._internalModel._relationships['pages']; equal(relationship.hasData, false, 'relationship does not have data'); }); }); @@ -1414,7 +1414,7 @@ test("hasMany hasData async created", function () { run(function() { var chapter = store.createRecord('chapter', { title: 'The Story Begins' }); - var relationship = chapter._relationships['pages']; + var relationship = chapter._internalModel._relationships['pages']; equal(relationship.hasData, true, 'relationship has data'); }); }); @@ -1424,7 +1424,7 @@ test("hasMany hasData sync created", function () { run(function() { var chapter = store.createRecord('chapter', { title: 'The Story Begins' }); - var relationship = chapter._relationships['pages']; + var relationship = chapter._internalModel._relationships['pages']; equal(relationship.hasData, true, 'relationship has data'); }); }); diff --git a/packages/ember-data/tests/integration/serializers/embedded-records-mixin-test.js b/packages/ember-data/tests/integration/serializers/embedded-records-mixin-test.js index 0c4f1e99a71..b2e4db91a08 100644 --- a/packages/ember-data/tests/integration/serializers/embedded-records-mixin-test.js +++ b/packages/ember-data/tests/integration/serializers/embedded-records-mixin-test.js @@ -609,6 +609,32 @@ test("serialize with embedded objects (hasMany relationship)", function() { }); }); +test("serialize with embedded objects (unknown hasMany relationship)", function() { + var league; + run(function() { + league = env.store.push('home-planet', { name: "Villain League", id: "123" }); + }); + + env.registry.register('serializer:home-planet', DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + villains: { embedded: 'always' } + } + })); + + var serializer, json; + warns(function() { + run(function() { + serializer = env.container.lookup("serializer:home-planet"); + json = serializer.serialize(league._createSnapshot()); + }); + }, /The embedded relationship 'villains' is undefined for 'home-planet' with id '123'. Please include it in your original payload./); + + deepEqual(json, { + name: "Villain League", + villains: [] + }); +}); + test("serialize with embedded objects (hasMany relationship) supports serialize:false", function() { run(function() { league = env.store.createRecord('home-planet', { name: "Villain League", id: "123" }); @@ -1165,6 +1191,63 @@ test("extractSingle with polymorphic hasMany", function() { }); +test("extractSingle with polymorphic hasMany and custom primary key", function() { + SuperVillain.reopen({ + secretWeapons: DS.hasMany("secretWeapon", { polymorphic: true }) + }); + + env.registry.register('adapter:super-villain', DS.ActiveModelAdapter); + env.registry.register('serializer:light-saber', DS.ActiveModelSerializer.extend({ + primaryKey: 'custom' + })); + env.registry.register('serializer:super-villain', DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + secretWeapons: { embedded: 'always' } + } + })); + var serializer = env.container.lookup("serializer:super-villain"); + + var json_hash = { + super_villain: { + id: "1", + first_name: "Tom", + last_name: "Dale", + secret_weapons: [ + { + custom: "1", + type: "LightSaber", + name: "Tom's LightSaber", + color: "Red" + }, + { + id: "1", + type: "SecretWeapon", + name: "The Death Star" + } + ] + } + }; + var json; + + run(function() { + json = serializer.extractSingle(env.store, SuperVillain, json_hash); + }); + + deepEqual(json, { + id: "1", + firstName: "Tom", + lastName: "Dale", + secretWeapons: [ + { id: "1", type: "light-saber" }, + { id: "1", type: "secret-weapon" } + ] + }, "Custom primary key of embedded hasMany is correctly normalized"); + + equal(env.store.recordForId("lightSaber", "1").get("name"), "Tom's LightSaber", "Embedded polymorphic LightSaber with custom primary key is found"); + equal(env.store.recordForId("secretWeapon", "1").get("name"), "The Death Star", "Embedded polymorphic SecretWeapon found"); + +}); + test("extractSingle with polymorphic belongsTo", function() { expect(2); @@ -1211,6 +1294,55 @@ test("extractSingle with polymorphic belongsTo", function() { }); +test("extractSingle with polymorphic belongsTo and custom primary key", function() { + expect(2); + + SuperVillain.reopen({ + secretLab: DS.belongsTo("secretLab", { polymorphic: true }) + }); + + env.registry.register('adapter:super-villain', DS.ActiveModelAdapter); + env.registry.register('serializer:super-villain', DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + secretLab: { embedded: 'always' } + } + })); + env.registry.register('serializer:bat-cave', DS.ActiveModelSerializer.extend({ + primaryKey: 'custom' + })); + var serializer = env.container.lookup("serializer:super-villain"); + + var json_hash = { + super_villain: { + id: "1", + first_name: "Tom", + last_name: "Dale", + secret_lab: { + custom: "1", + type: "bat-cave", + infiltrated: true + } + } + }; + + var json; + + run(function() { + json = serializer.extractSingle(env.store, SuperVillain, json_hash); + }); + + deepEqual(json, { + id: "1", + firstName: "Tom", + lastName: "Dale", + secretLab: "1", + secretLabType: "bat-cave" + }, "Custom primary key is correctly normalized"); + + equal(env.store.recordForId("batCave", "1").get("infiltrated"), true, "Embedded polymorphic BatCave with custom primary key is found"); + +}); + test("Mixin can be used with RESTSerializer which does not define keyForAttribute", function() { run(function() { homePlanet = env.store.createRecord('home-planet', { name: "Villain League", id: "123" }); diff --git a/packages/ember-data/tests/integration/snapshot-test.js b/packages/ember-data/tests/integration/snapshot-test.js index 0c71dffd273..8b85621cab2 100644 --- a/packages/ember-data/tests/integration/snapshot-test.js +++ b/packages/ember-data/tests/integration/snapshot-test.js @@ -112,6 +112,20 @@ test("snapshot.attributes() returns a copy of all attributes for the current sna }); }); +test("snapshot.changedAttributes() returns a copy of all changed attributes for the current snapshot", function() { + expect(1); + + run(function() { + var post = env.store.push('post', { id: 1, title: 'Hello World' }); + post.set('title', 'Hello World!'); + var snapshot = post._createSnapshot(); + + var changes = snapshot.changedAttributes(); + + deepEqual(changes, { title: ['Hello World', 'Hello World!'] }, 'changed attributes are returned correctly'); + }); +}); + test("snapshot.belongsTo() returns undefined if relationship is undefined", function() { expect(1); diff --git a/packages/ember-data/tests/unit/model-test.js b/packages/ember-data/tests/unit/model-test.js index 8063ea1dde8..e41eb4a38fc 100644 --- a/packages/ember-data/tests/unit/model-test.js +++ b/packages/ember-data/tests/unit/model-test.js @@ -147,17 +147,19 @@ test("a collision of a record's id with object function's name", function() { } }); -test("it should use `_reference` and not `reference` to store its reference", function() { +/* +test("it should use `_internalModel` and not `internalModel` to store its internalModel", function() { expect(1); run(function() { store.push('person', { id: 1 }); - store.find('person', 1).then(function(record) { - equal(record.get('reference'), undefined, "doesn't shadow reference key"); + store.find(Person, 1).then(function(record) { + equal(record.get('_internalModel'), undefined, "doesn't shadow internalModel key"); }); }); }); +*/ test("it should cache attributes", function() { expect(2); @@ -456,15 +458,15 @@ test("setting a property back to its original value removes the property from th run(function() { store.find('person', 1).then(function(person) { - equal(person._attributes.name, undefined, "the `_attributes` hash is clean"); + equal(person._internalModel._attributes.name, undefined, "the `_attributes` hash is clean"); set(person, 'name', "Niceguy Dale"); - equal(person._attributes.name, "Niceguy Dale", "the `_attributes` hash contains the changed value"); + equal(person._internalModel._attributes.name, "Niceguy Dale", "the `_attributes` hash contains the changed value"); set(person, 'name', "Scumbag Dale"); - equal(person._attributes.name, undefined, "the `_attributes` hash is reset"); + equal(person._internalModel._attributes.name, undefined, "the `_attributes` hash is reset"); }); }); }); @@ -825,3 +827,9 @@ test("Pushing a record into the store should transition it to the loaded state", equal(person.get('isNew'), false, 'push should put records into the loaded state'); }); }); + +test("A subclass of DS.Model throws an error when calling create() directly", function() { + throws(function() { + Person.create(); + }, /You should not call `create` on a model/, "Throws an error when calling create() on model"); +}); diff --git a/packages/ember-data/tests/unit/model/internal-model-test.js b/packages/ember-data/tests/unit/model/internal-model-test.js new file mode 100644 index 00000000000..5e81ff5ab17 --- /dev/null +++ b/packages/ember-data/tests/unit/model/internal-model-test.js @@ -0,0 +1,19 @@ +module("unit/model/internal-model - Internal Model"); + +var mockModelFactory = { + _create: function() { + return { trigger: function() {} }; + }, + + eachRelationship: function() { + } +}; +test("Materializing a model twice errors out", function() { + expect(1); + var internalModel = new DS.InternalModel(mockModelFactory, null, null, null); + + internalModel.materializeRecord(); + expectAssertion(function() { + internalModel.materializeRecord(); + }, /more than once/); +}); diff --git a/packages/ember-data/tests/unit/model/lifecycle-callbacks-test.js b/packages/ember-data/tests/unit/model/lifecycle-callbacks-test.js index 45cbd32c730..accac509a36 100644 --- a/packages/ember-data/tests/unit/model/lifecycle-callbacks-test.js +++ b/packages/ember-data/tests/unit/model/lifecycle-callbacks-test.js @@ -32,6 +32,32 @@ test("a record receives a didLoad callback when it has finished loading", functi }); }); +test("TEMPORARY: a record receives a didLoad callback once it materializes if it wasn't materialized when loaded", function() { + expect(2); + var didLoadCalled = 0; + var Person = DS.Model.extend({ + name: DS.attr(), + didLoad: function() { + didLoadCalled++; + } + }); + + var store = createStore({ + person: Person + }); + + run(function() { + store._pushInternalModel('person', { id: 1 }); + equal(didLoadCalled, 0, "didLoad was not called"); + }); + run(function() { + store.getById('person', 1); + }); + run(function() { + equal(didLoadCalled, 1, "didLoad was called"); + }); +}); + test("a record receives a didUpdate callback when it has finished updating", function() { expect(5); diff --git a/packages/ember-data/tests/unit/model/merge-test.js b/packages/ember-data/tests/unit/model/merge-test.js index 353099942a3..df3bfee770c 100644 --- a/packages/ember-data/tests/unit/model/merge-test.js +++ b/packages/ember-data/tests/unit/model/merge-test.js @@ -43,6 +43,41 @@ test("When a record is in flight, changes can be made", function() { }); }); +test("Make sure snapshot is created at save time not at flush time", function() { + expect(5); + + var adapter = DS.Adapter.extend({ + updateRecord: function(store, type, snapshot) { + equal(snapshot.attr('name'), 'Thomas Dale'); + + return Ember.RSVP.resolve(); + } + }); + + var store = createStore({ adapter: adapter, person: Person }); + var person; + + run(function() { + person = store.push('person', { id: 1, name: "Tom" }); + person.set('name', "Thomas Dale"); + }); + + run(function() { + var promise = person.save(); + + equal(person.get('name'), "Thomas Dale"); + + person.set('name', "Tomasz Dale"); + + equal(person.get('name'), "Tomasz Dale", "the local changes applied on top"); + + promise.then(async(function(person) { + equal(person.get('isDirty'), true, "The person is still dirty"); + equal(person.get('name'), "Tomasz Dale", "The local changes apply"); + })); + }); +}); + test("When a record is in flight, pushes are applied underneath the in flight changes", function() { expect(6); diff --git a/packages/ember-data/tests/unit/model/relationships/record-array-test.js b/packages/ember-data/tests/unit/model/relationships/record-array-test.js index 76809505d26..be273c2a158 100644 --- a/packages/ember-data/tests/unit/model/relationships/record-array-test.js +++ b/packages/ember-data/tests/unit/model/relationships/record-array-test.js @@ -11,18 +11,19 @@ test("updating the content of a RecordArray updates its content", function() { var env = setupStore({ tag: Tag }); var store = env.store; - var records, tags; + var records, tags, internalModel; run(function() { records = store.pushMany('tag', [{ id: 5, name: "friendly" }, { id: 2, name: "smarmy" }, { id: 12, name: "oohlala" }]); - tags = DS.RecordArray.create({ content: Ember.A(records.slice(0, 2)), store: store, type: Tag }); + internalModel = Ember.A(records).mapBy('_internalModel'); + tags = DS.RecordArray.create({ content: Ember.A(internalModel.slice(0, 2)), store: store, type: Tag }); }); var tag = tags.objectAt(0); equal(get(tag, 'name'), "friendly", "precond - we're working with the right tags"); run(function() { - set(tags, 'content', Ember.A(records.slice(1, 3))); + set(tags, 'content', Ember.A(internalModel.slice(1, 3))); }); tag = tags.objectAt(0); diff --git a/packages/ember-data/tests/unit/model/rollback-test.js b/packages/ember-data/tests/unit/model/rollback-test.js index a7f795b0d44..6db9b6eb832 100644 --- a/packages/ember-data/tests/unit/model/rollback-test.js +++ b/packages/ember-data/tests/unit/model/rollback-test.js @@ -159,6 +159,51 @@ test("new record can be rollbacked", function() { equal(person.get('isDeleted'), true, "must be deleted"); }); +test("invalid new record can be rollbacked", function() { + var person; + var adapter = DS.RESTAdapter.extend({ + ajax: function(url, type, hash) { + var adapter = this; + + return new Ember.RSVP.Promise(function(resolve, reject) { + /* If InvalidError is passed back in the reject it will throw the + exception which will bubble up the call stack (crashing the test) + instead of hitting the failure route of the promise. + So wrapping the reject in an Ember.run.next makes it so save + completes without failure and the failure hits the failure route + of the promise instead of crashing the save. */ + Ember.run.next(function() { + reject(adapter.ajaxError({ name: 'is invalid' })); + }); + }); + }, + + ajaxError: function(jqXHR) { + return new DS.InvalidError(jqXHR); + } + }); + + env = setupStore({ person: Person, adapter: adapter }); + + run(function() { + person = env.store.createRecord('person', { id: 1 }); + }); + + equal(person.get('isNew'), true, "must be new"); + equal(person.get('isDirty'), true, "must be dirty"); + + run(function() { + person.save().then(null, async(function() { + equal(person.get('isValid'), false); + person.rollback(); + + equal(person.get('isNew'), false, "must not be new"); + equal(person.get('isDirty'), false, "must not be dirty"); + equal(person.get('isDeleted'), true, "must be deleted"); + })); + }); +}); + test("deleted record can be rollbacked", function() { var person, people; diff --git a/packages/ember-data/tests/unit/store/adapter-interop-test.js b/packages/ember-data/tests/unit/store/adapter-interop-test.js index 69f642600c5..55cd298e46b 100644 --- a/packages/ember-data/tests/unit/store/adapter-interop-test.js +++ b/packages/ember-data/tests/unit/store/adapter-interop-test.js @@ -46,7 +46,7 @@ test("Calling Store#find invokes its adapter#find", function() { find: function(store, type, id, snapshot) { ok(true, "Adapter#find was called"); equal(store, currentStore, "Adapter#find was called with the right store"); - equal(type, store.modelFor('type'), "Adapter#find was called with the type passed into Store#find"); + equal(type, store.modelFor('test'), "Adapter#find was called with the type passed into Store#find"); equal(id, 1, "Adapter#find was called with the id passed into Store#find"); equal(snapshot.id, '1', "Adapter#find was called with the record created from Store#find"); @@ -54,13 +54,12 @@ test("Calling Store#find invokes its adapter#find", function() { } }); - var currentStore = createStore({ - adapter: adapter, - type: DS.Model.extend() - }); + var currentType = DS.Model.extend(); + var currentStore = createStore({ adapter: adapter, test: currentType }); + run(function() { - currentStore.find('type', 1); + currentStore.find('test', 1); }); }); @@ -100,15 +99,13 @@ test("Returning a promise from `find` asynchronously loads data", function() { } }); - var currentStore = createStore({ - adapter: adapter, - type: DS.Model.extend({ - name: DS.attr('string') - }) + var currentType = DS.Model.extend({ + name: DS.attr('string') }); + var currentStore = createStore({ adapter: adapter, test: currentType }); run(function() { - currentStore.find('type', 1).then(async(function(object) { + currentStore.find('test', 1).then(async(function(object) { strictEqual(get(object, 'name'), "Scumbag Dale", "the data was pushed"); })); }); @@ -124,20 +121,18 @@ test("IDs provided as numbers are coerced to strings", function() { } }); - var currentStore = createStore({ - adapter: adapter, - type: DS.Model.extend({ - name: DS.attr('string') - }) + var currentType = DS.Model.extend({ + name: DS.attr('string') }); + var currentStore = createStore({ adapter: adapter, test: currentType }); run(function() { - currentStore.find('type', 1).then(async(function(object) { + currentStore.find('test', 1).then(async(function(object) { equal(typeof object.get('id'), 'string', "id was coerced to a string"); run(function() { - currentStore.push('type', { id: 2, name: "Scumbag Sam Saffron" }); + currentStore.push('test', { id: 2, name: "Scumbag Sam Saffron" }); }); - return currentStore.find('type', 2); + return currentStore.find('test', 2); })).then(async(function(object) { ok(object, "object was found"); equal(typeof object.get('id'), 'string', "id is a string despite being supplied and searched for as a number"); @@ -395,12 +390,12 @@ test("initial values of attributes can be passed in as the third argument to fin }); var store = createStore({ - person: Person, - adapter: adapter + adapter: adapter, + test: Person }); run(function() { - store.find('person', 1, { name: 'Test' }); + store.find('test', 1, { name: 'Test' }); }); }); @@ -614,16 +609,16 @@ test("store.scheduleFetchMany should not resolve until all the records are resol var store = createStore({ adapter: adapter, - person: Person, + test: Person, phone: Phone }); run(function() { - store.createRecord('person'); + store.createRecord('test'); }); var records = Ember.A([ - store.recordForId('person', 10), + store.recordForId('test', 10), store.recordForId('phone', 20), store.recordForId('phone', 21) ]); @@ -669,13 +664,13 @@ test("the store calls adapter.findMany according to groupings returned by adapte var store = createStore({ adapter: adapter, - person: Person + test: Person }); var records = Ember.A([ - store.recordForId('person', 10), - store.recordForId('person', 20), - store.recordForId('person', 21) + store.recordForId('test', 10), + store.recordForId('test', 20), + store.recordForId('test', 21) ]); run(function() { @@ -718,12 +713,12 @@ test("the promise returned by `scheduleFetch`, when it resolves, does not depend var store = createStore({ adapter: adapter, - person: Person + test: Person }); run(function () { - var davidPromise = store.find('person', 'david'); - var igorPromise = store.find('person', 'igor'); + var davidPromise = store.find('test', 'david'); + var igorPromise = store.find('test', 'igor'); igorPromise.then(async(function () { equal(davidResolved, false, "Igor did not need to wait for David"); @@ -767,12 +762,12 @@ test("the promise returned by `scheduleFetch`, when it rejects, does not depend var store = createStore({ adapter: adapter, - person: Person + test: Person }); run(function () { - var davidPromise = store.find('person', 'david'); - var igorPromise = store.find('person', 'igor'); + var davidPromise = store.find('test', 'david'); + var igorPromise = store.find('test', 'igor'); igorPromise.then(null, async(function () { equal(davidResolved, false, "Igor did not need to wait for David"); @@ -805,13 +800,13 @@ test("store.fetchRecord reject records that were not found, even when those requ var store = createStore({ adapter: adapter, - person: Person + test: Person }); warns(function() { run(function () { - var davidPromise = store.find('person', 'david'); - var igorPromise = store.find('person', 'igor'); + var davidPromise = store.find('test', 'david'); + var igorPromise = store.find('test', 'igor'); davidPromise.then(async(function () { ok(true, "David resolved"); diff --git a/packages/ember-data/tests/unit/store/get-by-id-test.js b/packages/ember-data/tests/unit/store/get-by-id-test.js new file mode 100644 index 00000000000..440101867a8 --- /dev/null +++ b/packages/ember-data/tests/unit/store/get-by-id-test.js @@ -0,0 +1,35 @@ +var env, store, Person; +var run = Ember.run; + +module("unit/store/getById - Store getById", { + setup: function() { + + Person = DS.Model.extend(); + Person.toString = function() { + return 'Person'; + }; + + env = setupStore({ + person: Person + }); + store = env.store; + }, + + teardown: function() { + Ember.run(store, 'destroy'); + } +}); + +test("getById should return the record if it is in the store ", function() { + + run(function() { + var person = store.push('person', { id: 1 }); + equal(person, store.getById('person', 1), 'getById only return the corresponding record in the store'); + }); +}); + +test("getById should return null if the record is not in the store ", function() { + run(function() { + equal(null, store.getById('person', 1), 'getById returns null if the corresponding record is not in the store'); + }); +}); diff --git a/packages/ember-data/tests/unit/store/unload-test.js b/packages/ember-data/tests/unit/store/unload-test.js index 96343773427..a3c95a2ff8b 100644 --- a/packages/ember-data/tests/unit/store/unload-test.js +++ b/packages/ember-data/tests/unit/store/unload-test.js @@ -36,7 +36,7 @@ test("unload a dirty record", function() { store.find('record', 1).then(function(record) { record.set('title', 'toto2'); - record.send('willCommit'); + record._internalModel.send('willCommit'); equal(get(record, 'isDirty'), true, "record is dirty"); @@ -46,7 +46,7 @@ test("unload a dirty record", function() { // force back into safe to unload mode. run(function() { - record.transitionTo('deleted.saved'); + record._internalModel.transitionTo('deleted.saved'); }); }); }); diff --git a/tests/ember-configuration.js b/tests/ember-configuration.js index 9c9faf4eb29..b0582a9eb85 100644 --- a/tests/ember-configuration.js +++ b/tests/ember-configuration.js @@ -65,7 +65,7 @@ } else { env.container.normalize = fn; } - } + }; var adapter = env.adapter = (options.adapter || DS.Adapter); delete options.adapter; diff --git a/tests/ember-data-setup.js b/tests/ember-data-setup.js index 7f57db55d0a..ce0686fb355 100644 --- a/tests/ember-data-setup.js +++ b/tests/ember-data-setup.js @@ -1,5 +1,3 @@ -/* globals syncForTest */ - ;(function(){ Ember.RSVP.configure('onerror', function(reason) { diff --git a/tests/index.html b/tests/index.html index e245e4d6f95..0a02c96ba86 100644 --- a/tests/index.html +++ b/tests/index.html @@ -44,9 +44,9 @@ diff --git a/tests/qunit-configuration.js b/tests/qunit-configuration.js index f0cfa53a705..fec07e42416 100644 --- a/tests/qunit-configuration.js +++ b/tests/qunit-configuration.js @@ -1,85 +1,87 @@ (function() { + /*global namespace: true */ + window.EmberDev = window.EmberDev || {}; EmberDev.afterEach = function() { - if (Ember && Ember.View) { - var viewIds = [], id; - for (id in Ember.View.views) { - if (Ember.View.views[id] != null) { - viewIds.push(id); - } + if (Ember && Ember.View) { + var viewIds = [], id; + for (id in Ember.View.views) { + if (Ember.View.views[id] != null) { + viewIds.push(id); } + } - if (viewIds.length > 0) { - deepEqual(viewIds, [], "Ember.View.views should be empty"); - Ember.View.views = []; - } + if (viewIds.length > 0) { + deepEqual(viewIds, [], "Ember.View.views should be empty"); + Ember.View.views = []; } + } - if (Ember && Ember.TEMPLATES) { - var templateNames = [], name; - for (name in Ember.TEMPLATES) { - if (Ember.TEMPLATES[name] != null) { - templateNames.push(name); - } + if (Ember && Ember.TEMPLATES) { + var templateNames = [], name; + for (name in Ember.TEMPLATES) { + if (Ember.TEMPLATES[name] != null) { + templateNames.push(name); } + } - if (templateNames.length > 0) { - deepEqual(templateNames, [], "Ember.TEMPLATES should be empty"); - Ember.TEMPLATES = {}; - } + if (templateNames.length > 0) { + deepEqual(templateNames, [], "Ember.TEMPLATES should be empty"); + Ember.TEMPLATES = {}; } - }; + } + }; - window.globalFailedTests = []; - window.globalTestResults = null; - window.lastAssertionTime = new Date().getTime(); + window.globalFailedTests = []; + window.globalTestResults = null; + window.lastAssertionTime = new Date().getTime(); - var currentTest, assertCount; + var currentTest, assertCount; - QUnit.testStart(function(data) { - // Reset the assertion count - assertCount = 0; + QUnit.testStart(function(data) { + // Reset the assertion count + assertCount = 0; - currentTest = { - name: data.name, - failedAssertions: [], - total: 0, - passed: 0, - failed: 0, - start: new Date(), - time: 0 - }; + currentTest = { + name: data.name, + failedAssertions: [], + total: 0, + passed: 0, + failed: 0, + start: new Date(), + time: 0 + }; - }) + }); - QUnit.log(function(data) { - assertCount++; - lastAssertionTime = new Date().getTime(); + QUnit.log(function(data) { + assertCount++; + lastAssertionTime = new Date().getTime(); - // Ignore passing assertions - if (!data.result) { - currentTest.failedAssertions.push(data); - } - }); + // Ignore passing assertions + if (!data.result) { + currentTest.failedAssertions.push(data); + } + }); - QUnit.testDone(function(data) { - currentTest.time = (new Date()).getTime() - currentTest.start.getTime(); // ms - currentTest.total = data.total; - currentTest.passed = data.passed; - currentTest.failed = data.failed; + QUnit.testDone(function(data) { + currentTest.time = (new Date()).getTime() - currentTest.start.getTime(); // ms + currentTest.total = data.total; + currentTest.passed = data.passed; + currentTest.failed = data.failed; - if (currentTest.failed > 0) - window.globalFailedTests.push(currentTest) + if (currentTest.failed > 0) + window.globalFailedTests.push(currentTest); - currentTest = null; - }); + currentTest = null; + }); - QUnit.done(function( details ) { - details.failedTests = globalFailedTests; + QUnit.done(function( details ) { + details.failedTests = globalFailedTests; - window.globalTestResults = details; - }); + window.globalTestResults = details; + }); // hack qunit to not suck for Ember objects var originalTypeof = QUnit.jsDump.typeOf; @@ -174,7 +176,7 @@ function MethodCallExpectation(target, property){ this.target = target; this.property = property; - }; + } MethodCallExpectation.prototype = { handleCall: function(){ @@ -208,7 +210,8 @@ function AssertExpectation(message){ MethodCallExpectation.call(this, Ember, 'assert'); this.expectedMessage = message; - }; + } + AssertExpectation.Error = function(){}; AssertExpectation.prototype = o_create(MethodCallExpectation.prototype); AssertExpectation.prototype.handleCall = function(message, test){ @@ -281,7 +284,7 @@ // Ember.deprecate("Old And Busted"); // window.expectNoDeprecation = function(message) { - if (typeof EmberDev.deprecations.expecteds === 'array') { + if (Ember.isArray(EmberDev.deprecations.expecteds)) { throw("No deprecation was expected after expectDeprecation was called!"); } EmberDev.deprecations.stubEmber(); @@ -344,15 +347,15 @@ if (expecteds === EmberDev.deprecations.NONE) { var actualMessages = []; - for (var actual in actuals) { - actualMessages.push(actual[0]); + for (var _actual in actuals) { + actualMessages.push(_actual[0]); } ok(actuals.length === 0, "Expected no deprecation call, got: "+actualMessages.join(', ')); } else { for (var o=0;o < expecteds.length; o++) { - var expected = expecteds[o], match; + var expected = expecteds[o], match, actual; for (var i=0;i < actuals.length; i++) { - var actual = actuals[i]; + actual = actuals[i]; if (!actual[1]) { if (expected instanceof RegExp) { if (expected.test(actual[0])) {