diff --git a/.travis.yml b/.travis.yml index dba38c0..f27fdfe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,21 +1,19 @@ --- language: node_js node_js: - - "6" - -sudo: false -dist: trusty + # we recommend testing addons with the same minimum supported node version as Ember CLI + # so that your addon works for all apps + - "8" +sudo: required addons: chrome: stable -cache: - directories: - - $HOME/.npm - before_script: - npm install grunt-cli bower - bower install +- export DISPLAY=:99.0 +# - sh -e /etc/init.d/xvfb start script: grunt test after_success: 'grunt build ember-s3' diff --git a/ember-model.js b/ember-model.js index 9404d8a..8265bd9 100644 --- a/ember-model.js +++ b/ember-model.js @@ -1,727 +1,728 @@ (function() { -var VERSION = '3.0.0'; + var VERSION = '0.0.18'; -if (Ember.libraries) { - Ember.libraries.register('Ember Model', VERSION); -} + if (Ember.libraries) { + Ember.libraries.register('Ember Model', VERSION); + } -})(); + })(); -(function() { + (function() { -function mustImplement(message) { - var fn = function() { - var className = this.constructor.toString(); + function mustImplement(message) { + var fn = function() { + var className = this.constructor.toString(); - throw new Error(message.replace('{{className}}', className)); - }; - fn.isUnimplemented = true; - return fn; -} - -Ember.Adapter = Ember.Object.extend({ - find: mustImplement('{{className}} must implement find'), - findQuery: mustImplement('{{className}} must implement findQuery'), - findMany: mustImplement('{{className}} must implement findMany'), - findAll: mustImplement('{{className}} must implement findAll'), - createRecord: mustImplement('{{className}} must implement createRecord'), - saveRecord: mustImplement('{{className}} must implement saveRecord'), - deleteRecord: mustImplement('{{className}} must implement deleteRecord'), - - load: function(record, id, data) { - record.load(id, data); + throw new Error(message.replace('{{className}}', className)); + }; + fn.isUnimplemented = true; + return fn; } -}); + Ember.Adapter = Ember.Object.extend({ + find: mustImplement('{{className}} must implement find'), + findQuery: mustImplement('{{className}} must implement findQuery'), + findMany: mustImplement('{{className}} must implement findMany'), + findAll: mustImplement('{{className}} must implement findAll'), + createRecord: mustImplement('{{className}} must implement createRecord'), + saveRecord: mustImplement('{{className}} must implement saveRecord'), + deleteRecord: mustImplement('{{className}} must implement deleteRecord'), -})(); - -(function() { + load: function(record, id, data) { + record.load(id, data); + } + }); -var get = Ember.get, - set = Ember.set; + Ember.Adapter.reopenClass({ + toString() { + return `Ember.Adapter`; + } + }); -Ember.FixtureAdapter = Ember.Adapter.extend({ - _counter: 0, - _findData: function(klass, id) { - var fixtures = klass.FIXTURES, - idAsString = id.toString(), - primaryKey = get(klass, 'primaryKey'), - data = Ember.A(fixtures).find(function(el) { return (el[primaryKey]).toString() === idAsString; }); - return data; - }, + })(); - _setPrimaryKey: function(record) { - var klass = record.constructor, - fixtures = klass.FIXTURES, - primaryKey = get(klass, 'primaryKey'); + (function() { + var get = Ember.get, + set = Ember.set; - if(record.get(primaryKey)) { - return; - } + Ember.FixtureAdapter = Ember.Adapter.extend({ + _counter: 0, + _findData: function(klass, id) { + var fixtures = klass.FIXTURES, + idAsString = id.toString(), + primaryKey = get(klass, 'primaryKey'), + data = Ember.A(fixtures).find(function(el) { return (el[primaryKey]).toString() === idAsString; }); - set(record, primaryKey, this._generatePrimaryKey()); - }, + return data; + }, - _generatePrimaryKey: function() { - var counter = this.get("_counter"); + _setPrimaryKey: function(record) { + var klass = record.constructor, + fixtures = klass.FIXTURES, + primaryKey = get(klass, 'primaryKey'); - this.set("_counter", counter + 1); - return "fixture-" + counter; - }, + if(record.get(primaryKey)) { + return; + } - find: function(record, id) { - var data = this._findData(record.constructor, id); + set(record, primaryKey, this._generatePrimaryKey()); + }, - return new Ember.RSVP.Promise(function(resolve, reject) { - Ember.run.later(this, function() { - Ember.run(record, record.load, id, data); - resolve(record); - }, 0); - }); - }, + _generatePrimaryKey: function() { + var counter = this.get("_counter"); - findMany: function(klass, records, ids) { - var fixtures = klass.FIXTURES, - requestedData = []; + this.set("_counter", counter + 1); - for (var i = 0, l = ids.length; i < l; i++) { - requestedData.push(this._findData(klass, ids[i])); - } + return "fixture-" + counter; + }, - return new Ember.RSVP.Promise(function(resolve, reject) { - Ember.run.later(this, function() { - Ember.run(records, records.load, klass, requestedData); - resolve(records); - }, 0); - }); - }, + find: function(record, id) { + var data = this._findData(record.constructor, id); - findAll: function(klass, records) { - var fixtures = klass.FIXTURES; + return new Ember.RSVP.Promise(function(resolve, reject) { + Ember.run.later(this, function() { + Ember.run(record, record.load, id, data); + resolve(record); + }, 0); + }); + }, - return new Ember.RSVP.Promise(function(resolve, reject) { - Ember.run.later(this, function() { - Ember.run(records, records.load, klass, fixtures); - resolve(records); - }, 0); - }); - }, - - createRecord: function(record) { - var klass = record.constructor, - fixtures = klass.FIXTURES, - self = this; - - return new Ember.RSVP.Promise(function(resolve, reject) { - Ember.run.later(this, function() { - var rootKey = record.constructor.rootKey, - json; - - self._setPrimaryKey(record); - json = rootKey ? record.toJSON()[rootKey] : record.toJSON(); - fixtures.push(klass.findFromCacheOrLoad(json)); - record.didCreateRecord(); - resolve(record); - }, 0); - }); - }, - - saveRecord: function(record) { - return new Ember.RSVP.Promise(function(resolve, reject) { - Ember.run.later(this, function() { - record.didSaveRecord(); - resolve(record); - }, 0); - }); - }, - - deleteRecord: function(record) { - return new Ember.RSVP.Promise(function(resolve, reject) { - Ember.run.later(this, function() { - record.didDeleteRecord(); - resolve(record); - }, 0); - }); - } -}); + findMany: function(klass, records, ids) { + var fixtures = klass.FIXTURES, + requestedData = []; + for (var i = 0, l = ids.length; i < l; i++) { + requestedData.push(this._findData(klass, ids[i])); + } -})(); + return new Ember.RSVP.Promise(function(resolve, reject) { + Ember.run.later(this, function() { + Ember.run(records, records.load, klass, requestedData); + resolve(records); + }, 0); + }); + }, -(function() { + findAll: function(klass, records) { + var fixtures = klass.FIXTURES; -var get = Ember.get, - set = Ember.set; - -Ember.RecordArray = Ember.ArrayProxy.extend(Ember.Evented, { - isLoaded: false, - isLoading: Ember.computed.not('isLoaded'), - - load: function(klass, data) { - set(this, 'content', this.materializeData(klass, data)); - this.notifyLoaded(); - }, - - loadForFindMany: function(klass) { - var self = this; - var content = get(this, '_ids').map(function(id) { return klass.cachedRecordForId(id, Ember.getOwner(self)); }); - set(this, 'content', Ember.A(content)); - this.notifyLoaded(); - }, - - notifyLoaded: function() { - set(this, 'isLoaded', true); - this.trigger('didLoad'); - }, - - materializeData: function(klass, data) { - var self = this; - var owner = Ember.getOwner(this); - return Ember.A(data.map(function(el) { - return klass.findFromCacheOrLoad(el, owner); - })); - }, - - reload: function() { - var modelClass = this.get('modelClass'), - self = this, - promises; - - set(this, 'isLoaded', false); - if (modelClass._findAllRecordArray === this) { - return modelClass.adapter.findAll(modelClass, this); - } else if (this._query) { - return modelClass.adapter.findQuery(modelClass, this, this._query); - } else { - promises = this.map(function(record) { - return record.reload(); - }); - return Ember.RSVP.all(promises).then(function(data) { - self.notifyLoaded(); + return new Ember.RSVP.Promise(function(resolve, reject) { + Ember.run.later(this, function() { + Ember.run(records, records.load, klass, fixtures); + resolve(records); + }, 0); }); - } - } -}); + }, + createRecord: function(record) { + var klass = record.constructor, + fixtures = klass.FIXTURES, + self = this; -})(); + return new Ember.RSVP.Promise(function(resolve, reject) { + Ember.run.later(this, function() { + var rootKey = record.constructor.rootKey, + json; -(function() { + self._setPrimaryKey(record); + json = rootKey ? record.toJSON()[rootKey] : record.toJSON(); + fixtures.push(klass.findFromCacheOrLoad(json)); + record.didCreateRecord(); + resolve(record); + }, 0); + }); + }, -var get = Ember.get; + saveRecord: function(record) { + return new Ember.RSVP.Promise(function(resolve, reject) { + Ember.run.later(this, function() { + record.didSaveRecord(); + resolve(record); + }, 0); + }); + }, -Ember.FilteredRecordArray = Ember.RecordArray.extend({ - init: function() { - if (!get(this, 'modelClass')) { - throw new Error('FilteredRecordArrays must be created with a modelClass'); - } - if (!get(this, 'filterFunction')) { - throw new Error('FilteredRecordArrays must be created with a filterFunction'); - } - if (!get(this, 'filterProperties')) { - throw new Error('FilteredRecordArrays must be created with filterProperties'); + deleteRecord: function(record) { + return new Ember.RSVP.Promise(function(resolve, reject) { + Ember.run.later(this, function() { + record.didDeleteRecord(); + resolve(record); + }, 0); + }); } + }); - var modelClass = get(this, 'modelClass'); - modelClass.registerRecordArray(this); - this.registerObservers(); - this.updateFilter(); + })(); - this._super(); - }, + (function() { - updateFilter: function() { - var self = this, - results = []; - get(this, 'modelClass').forEachCachedRecord(function(record) { - if (self.filterFunction(record)) { - results.push(record); - } - }); - this.set('content', Ember.A(results)); - }, + var get = Ember.get, + set = Ember.set; - updateFilterForRecord: function(record) { - var results = get(this, 'content'); - if (this.filterFunction(record) && !results.includes(record)) { - results.pushObject(record); - } - }, + Ember.RecordArray = Ember.ArrayProxy.extend(Ember.Evented, { + isLoaded: false, + isLoading: Ember.computed.not('isLoaded'), - registerObservers: function() { - var self = this; - get(this, 'modelClass').forEachCachedRecord(function(record) { - self.registerObserversOnRecord(record); - }); - }, + load: function(klass, data) { + set(this, 'content', this.materializeData(klass, data)); + this.notifyLoaded(); + }, - registerObserversOnRecord: function(record) { - var self = this, - filterProperties = get(this, 'filterProperties'); + loadForFindMany: function(klass) { + var self = this; + var content = get(this, '_ids').map(function(id) { return klass.cachedRecordForId(id, self.container); }); + set(this, 'content', Ember.A(content)); + this.notifyLoaded(); + }, - for (var i = 0, l = get(filterProperties, 'length'); i < l; i++) { - record.addObserver(filterProperties[i], self, 'updateFilterForRecord'); + notifyLoaded: function() { + set(this, 'isLoaded', true); + this.trigger('didLoad'); + }, + + materializeData: function(klass, data) { + var self = this; + return Ember.A(data.map(function(el) { + return klass.findFromCacheOrLoad(el, self.container); // FIXME + })); + }, + + reload: function() { + var modelClass = this.get('modelClass'), + self = this, + promises; + + set(this, 'isLoaded', false); + if (modelClass._findAllRecordArray === this) { + return modelClass.adapter.findAll(modelClass, this); + } else if (this._query) { + return modelClass.adapter.findQuery(modelClass, this, this._query); + } else { + promises = this.map(function(record) { + return record.reload(); + }); + return Ember.RSVP.all(promises).then(function(data) { + self.notifyLoaded(); + }); + } } - } -}); + }); -})(); + })(); -(function() { + (function() { -var get = Ember.get, set = Ember.set; + var get = Ember.get; -Ember.ManyArray = Ember.RecordArray.extend({ - _records: null, - originalContent: null, - _modifiedRecords: null, + Ember.FilteredRecordArray = Ember.RecordArray.extend({ + init: function() { + if (!get(this, 'modelClass')) { + throw new Error('FilteredRecordArrays must be created with a modelClass'); + } + if (!get(this, 'filterFunction')) { + throw new Error('FilteredRecordArrays must be created with a filterFunction'); + } + if (!get(this, 'filterProperties')) { + throw new Error('FilteredRecordArrays must be created with filterProperties'); + } - unloadObject: function(record) { - var obj = get(this, 'content').findBy('clientId', record._reference.clientId); - get(this, 'content').removeObject(obj); + var modelClass = get(this, 'modelClass'); + modelClass.registerRecordArray(this); - var originalObj = get(this, 'originalContent').findBy('clientId', record._reference.clientId); - get(this, 'originalContent').removeObject(originalObj); - }, + this.registerObservers(); + this.updateFilter(); - isDirty: Ember.computed('content.[]', 'originalContent.[]', '_modifiedRecords.[]', function() { - var originalContent = get(this, 'originalContent'), - originalContentLength = get(originalContent, 'length'), - content = get(this, 'content'), - contentLength = get(content, 'length'); + this._super(); + }, - if (originalContentLength !== contentLength) { return true; } + updateFilter: function() { + var self = this, + results = []; + get(this, 'modelClass').forEachCachedRecord(function(record) { + if (self.filterFunction(record)) { + results.push(record); + } + }); + this.set('content', Ember.A(results)); + }, - if (this._modifiedRecords && this._modifiedRecords.length) { return true; } + updateFilterForRecord: function(record) { + var results = get(this, 'content'); + if (this.filterFunction(record) && !results.includes(record)) { + results.pushObject(record); + } + }, - var isDirty = false; + registerObservers: function() { + var self = this; + get(this, 'modelClass').forEachCachedRecord(function(record) { + self.registerObserversOnRecord(record); + }); + }, + + registerObserversOnRecord: function(record) { + var self = this, + filterProperties = get(this, 'filterProperties'); - for (var i = 0, l = contentLength; i < l; i++) { - if (!originalContent.includes(content[i])) { - isDirty = true; - break; + for (var i = 0, l = get(filterProperties, 'length'); i < l; i++) { + record.addObserver(filterProperties[i], self, 'updateFilterForRecord'); } } + }); - return isDirty; - }), - objectAtContent: function(idx) { - var content = get(this, 'content'); + })(); - if (!content.length || idx >= content.length) { return; } + (function() { - // need to add observer if it wasn't materialized before - var observerNeeded = (content[idx].record) ? false : true; + var get = Ember.get, set = Ember.set; - var owner = Ember.getOwner(this); - var record = this.materializeRecord(idx, owner); + Ember.ManyArray = Ember.RecordArray.extend({ + _records: null, + originalContent: null, + _modifiedRecords: null, - if (observerNeeded) { - var isDirtyRecord = record.get('isDirty'), isNewRecord = record.get('isNew'); - if (isDirtyRecord || isNewRecord) { this._modifiedRecords.pushObject(content[idx]); } - Ember.addObserver(content[idx], 'record.isDirty', this, 'recordStateChanged'); - record.registerParentHasManyArray(this); - } + unloadObject: function(record) { + var obj = get(this, 'content').findBy('clientId', record._reference.clientId); + get(this, 'content').removeObject(obj); - return record; - }, + var originalObj = get(this, 'originalContent').findBy('clientId', record._reference.clientId); + get(this, 'originalContent').removeObject(originalObj); + }, - save: function() { - // TODO: loop over dirty records only - return Ember.RSVP.all(this.map(function(record) { - return record.save(); - })).then(Ember.A); - }, + isDirty: Ember.computed('content.[]', 'originalContent.[]', '_modifiedRecords.[]', function() { + var originalContent = get(this, 'originalContent'), + originalContentLength = get(originalContent, 'length'), + content = get(this, 'content'), + contentLength = get(content, 'length'); - replaceContent: function(index, removed, added) { - added = added.map(function(record) { - return record._reference; - }); + if (originalContentLength !== contentLength) { return true; } - this._super(index, removed, added); - }, + if (this._modifiedRecords && this._modifiedRecords.length) { return true; } - _contentDidChange: Ember.observer('content', function() { - var content = get(this, 'content'); - var contentPrev = this._content; + var isDirty = false; - if (contentPrev && contentPrev !== content) { - this.arrayWillChange(contentPrev, 0, get(contentPrev, 'length'), 0); - contentPrev.removeArrayObserver(this); - this._setupOriginalContent(content); - } + for (var i = 0, l = contentLength; i < l; i++) { + if (!originalContent.includes(content[i])) { + isDirty = true; + break; + } + } - if (content) { - content.addArrayObserver(this); - this.arrayDidChange(content, 0, 0, get(content, 'length')); - } + return isDirty; + }), - this._content = content; - }), + objectAtContent: function(idx) { + var content = get(this, 'content'); - arrayWillChange: function(item, idx, removedCnt, addedCnt) { - var content = item; - for (var i = idx; i < idx+removedCnt; i++) { - var currentItem = content[i]; - if (currentItem && currentItem.record) { - this._modifiedRecords.removeObject(currentItem); - currentItem.record.unregisterParentHasManyArray(this); - Ember.removeObserver(currentItem, 'record.isDirty', this, 'recordStateChanged'); - } - } - }, + if (!content.length || idx >= content.length) { return; } + + // need to add observer if it wasn't materialized before + var observerNeeded = (content[idx].record) ? false : true; - arrayDidChange: function(item, idx, removedCnt, addedCnt) { - var parent = get(this, 'parent'), relationshipKey = get(this, 'relationshipKey'), - isDirty = get(this, 'isDirty'); + var record = this.materializeRecord(idx, this.container); - var content = item; - for (var i = idx; i < idx+addedCnt; i++) { - var currentItem = content[i]; - if (currentItem && currentItem.record) { - var isDirtyRecord = currentItem.record.get('isDirty'), isNewRecord = currentItem.record.get('isNew'); // why newly created object is not dirty? - if (isDirtyRecord || isNewRecord) { this._modifiedRecords.pushObject(currentItem); } - Ember.addObserver(currentItem, 'record.isDirty', this, 'recordStateChanged'); - currentItem.record.registerParentHasManyArray(this); + if (observerNeeded) { + var isDirtyRecord = record.get('isDirty'), isNewRecord = record.get('isNew'); + if (isDirtyRecord || isNewRecord) { this._modifiedRecords.pushObject(content[idx]); } + Ember.addObserver(content[idx], 'record.isDirty', this, 'recordStateChanged'); + record.registerParentHasManyArray(this); } - } - if (isDirty) { - parent._relationshipBecameDirty(relationshipKey); - } else { - parent._relationshipBecameClean(relationshipKey); - } - }, + return record; + }, - load: function(content) { - Ember.setProperties(this, { - content: content, - originalContent: Ember.A(content.slice()) - }); - set(this, '_modifiedRecords', Ember.A([])); - }, + save: function() { + // TODO: loop over dirty records only + return Ember.RSVP.all(this.map(function(record) { + return record.save(); + })).then(Ember.A); + }, - revert: function() { - this._setupOriginalContent(); - }, + replaceContent: function(index, removed, added) { + added = added.map(function(record) { + return record._reference; + }); - _setupOriginalContent: function(content) { - content = content || get(this, 'content'); - if (content) { - set(this, 'originalContent', Ember.A(content.slice())); - } - set(this, '_modifiedRecords', Ember.A([])); - }, + this._super(index, removed, added); + }, - init: function() { - this._super(); - this._setupOriginalContent(); - this._contentDidChange(); - }, + _contentDidChange: Ember.observer('content', function() { + var content = get(this, 'content'); + var contentPrev = this._content; - recordStateChanged: function(obj, keyName) { - var parent = get(this, 'parent'), relationshipKey = get(this, 'relationshipKey'); + if (contentPrev && contentPrev !== content) { + this.arrayWillChange(contentPrev, 0, get(contentPrev, 'length'), 0); + contentPrev.removeArrayObserver(this); + this._setupOriginalContent(content); + } - if (obj.record.get('isDirty')) { - if (this._modifiedRecords.indexOf(obj) === -1) { this._modifiedRecords.pushObject(obj); } - parent._relationshipBecameDirty(relationshipKey); - } else { - if (this._modifiedRecords.indexOf(obj) > -1) { this._modifiedRecords.removeObject(obj); } - if (!this.get('isDirty')) { + if (content) { + content.addArrayObserver(this); + this.arrayDidChange(content, 0, 0, get(content, 'length')); + } + + this._content = content; + }), + + arrayWillChange: function(item, idx, removedCnt, addedCnt) { + var content = item; + for (var i = idx; i < idx+removedCnt; i++) { + var currentItem = content[i]; + if (currentItem && currentItem.record) { + this._modifiedRecords.removeObject(currentItem); + currentItem.record.unregisterParentHasManyArray(this); + Ember.removeObserver(currentItem, 'record.isDirty', this, 'recordStateChanged'); + } + } + }, + + arrayDidChange: function(item, idx, removedCnt, addedCnt) { + var parent = get(this, 'parent'), relationshipKey = get(this, 'relationshipKey'), + isDirty = get(this, 'isDirty'); + + var content = item; + for (var i = idx; i < idx+addedCnt; i++) { + var currentItem = content[i]; + if (currentItem && currentItem.record) { + var isDirtyRecord = currentItem.record.get('isDirty'), isNewRecord = currentItem.record.get('isNew'); // why newly created object is not dirty? + if (isDirtyRecord || isNewRecord) { this._modifiedRecords.pushObject(currentItem); } + Ember.addObserver(currentItem, 'record.isDirty', this, 'recordStateChanged'); + currentItem.record.registerParentHasManyArray(this); + } + } + + if (isDirty) { + parent._relationshipBecameDirty(relationshipKey); + } else { parent._relationshipBecameClean(relationshipKey); } - } - } -}); + }, -Ember.HasManyArray = Ember.ManyArray.extend({ - materializeRecord: function(idx, owner) { - var klass = get(this, 'modelClass'), - content = get(this, 'content'), - reference = content.objectAt(idx), - record = reference.record; + load: function(content) { + Ember.setProperties(this, { + content: content, + originalContent: Ember.A(content.slice()) + }); + set(this, '_modifiedRecords', Ember.A([])); + }, - if (record) { - Ember.setOwner(record, owner); - return record; + revert: function() { + this._setupOriginalContent(); + }, + + _setupOriginalContent: function(content) { + content = content || get(this, 'content'); + if (content) { + set(this, 'originalContent', Ember.A(content.slice())); + } + set(this, '_modifiedRecords', Ember.A([])); + }, + + init: function() { + this._super(); + this._setupOriginalContent(); + this._contentDidChange(); + }, + + recordStateChanged: function(obj, keyName) { + var parent = get(this, 'parent'), relationshipKey = get(this, 'relationshipKey'); + + if (obj.record.get('isDirty')) { + if (this._modifiedRecords.indexOf(obj) === -1) { this._modifiedRecords.pushObject(obj); } + parent._relationshipBecameDirty(relationshipKey); + } else { + if (this._modifiedRecords.indexOf(obj) > -1) { this._modifiedRecords.removeObject(obj); } + if (!this.get('isDirty')) { + parent._relationshipBecameClean(relationshipKey); + } + } } - return klass._findFetchById(reference.id, false, owner); - }, + }); - toJSON: function() { - var ids = [], content = this.get('content'); + Ember.HasManyArray = Ember.ManyArray.extend({ + materializeRecord: function(idx, container) { + var klass = get(this, 'modelClass'), + content = get(this, 'content'), + reference = content.objectAt(idx), + record = reference.record; - content.forEach(function(reference) { - if (reference.id) { - ids.push(reference.id); + if (record) { + if (! record.container) { + record.container = container; + } + return record; } - }); + return klass._findFetchById(reference.id, false, container); + }, - return ids; - } -}); + toJSON: function() { + var ids = [], content = this.get('content'); -Ember.EmbeddedHasManyArray = Ember.ManyArray.extend({ - create: function(attrs) { - var klass = get(this, 'modelClass'), - record = klass.create(attrs); + content.forEach(function(reference) { + if (reference.id) { + ids.push(reference.id); + } + }); - this.pushObject(record); + return ids; + } + }); - return record; // FIXME: inject parent's id - }, + Ember.EmbeddedHasManyArray = Ember.ManyArray.extend({ + create: function(attrs) { + var klass = get(this, 'modelClass'), + record = klass.create(attrs); - materializeRecord: function(idx, owner) { - var klass = get(this, 'modelClass'), - primaryKey = get(klass, 'primaryKey'), - content = get(this, 'content'), - reference = content.objectAt(idx), - attrs = reference.data; + this.pushObject(record); - var record; - if (reference.record) { - record = reference.record; - } else { - record = klass.create({ _reference: reference }); - reference.record = record; - Ember.setOwner(record, owner); + return record; // FIXME: inject parent's id + }, + + materializeRecord: function(idx, container) { + var klass = get(this, 'modelClass'), + primaryKey = get(klass, 'primaryKey'), + content = get(this, 'content'), + reference = content.objectAt(idx), + attrs = reference.data; - if (attrs) { - record.load(attrs[primaryKey], attrs); + var record; + if (reference.record) { + record = reference.record; + } else { + record = klass.create({ _reference: reference, container: container }); + reference.record = record; + if (attrs) { + record.load(attrs[primaryKey], attrs); + } } - } - return record; - }, + record.container = container; + return record; + }, - toJSON: function() { - return this.map(function(record) { - return record.toJSON(); - }); - } -}); + toJSON: function() { + return this.map(function(record) { + return record.toJSON(); + }); + } + }); -})(); + })(); -(function() { + (function() { -var get = Ember.get, - set = Ember.set, - setProperties = Ember.setProperties, - meta = Ember.meta, - underscore = Ember.String.underscore; + var get = Ember.get, + set = Ember.set, + setProperties = Ember.setProperties, + meta = Ember.meta, + underscore = Ember.String.underscore; -function contains(array, element) { - for (var i = 0, l = array.length; i < l; i++) { - if (array[i] === element) { return true; } - } - return false; -} - -function concatUnique(toArray, fromArray) { - var e; - for (var i = 0, l = fromArray.length; i < l; i++) { - e = fromArray[i]; - if (!contains(toArray, e)) { toArray.push(e); } + function contains(array, element) { + for (var i = 0, l = array.length; i < l; i++) { + if (array[i] === element) { return true; } + } + return false; } - return toArray; -} -function hasCachedValue(object, key) { - var objectMeta = meta(object, false); - if (objectMeta) { - return key in objectMeta.cache; + function concatUnique(toArray, fromArray) { + var e; + for (var i = 0, l = fromArray.length; i < l; i++) { + e = fromArray[i]; + if (!contains(toArray, e)) { toArray.push(e); } + } + return toArray; } -} -function isDescriptor(value) { - // Ember < 1.11 - if (Ember.Descriptor !== undefined) { - return value instanceof Ember.Descriptor; - } - // Ember >= 1.11 - return value && typeof value === 'object' && value.isDescriptor; -} - -Ember.run.queues.push('data'); - -Ember.Model = Ember.Object.extend(Ember.Evented, { - isLoaded: true, - isLoading: Ember.computed.not('isLoaded'), - isNew: true, - isDeleted: false, - _dirtyAttributes: null, - - /** - Called when attribute is accessed. - - @method getAttr - @param key {String} key which is being accessed - @param value {Object} value, which will be returned from getter by default - */ - getAttr: function(key, value) { - return value; - }, - - isDirty: Ember.computed('_dirtyAttributes.length', function() { - var dirtyAttributes = get(this, '_dirtyAttributes'); - return dirtyAttributes && dirtyAttributes.length !== 0 || false; - }), - - _relationshipBecameDirty: function(name) { - var dirtyAttributes = get(this, '_dirtyAttributes'); - if (dirtyAttributes) { - dirtyAttributes.addObject(name); - } else { - set(this, '_dirtyAttributes', Ember.A([name])); - } - }, - - _relationshipBecameClean: function(name) { - var dirtyAttributes = get(this, '_dirtyAttributes'); - if (dirtyAttributes) { dirtyAttributes.removeObject(name); } - }, - - dataKey: function(key) { - var camelizeKeys = get(this.constructor, 'camelizeKeys'); - var meta = this.constructor.metaForProperty(key); - if (meta.options && meta.options.key) { - return camelizeKeys ? underscore(meta.options.key) : meta.options.key; + function hasCachedValue(object, key) { + var objectMeta = meta(object, false); + if (objectMeta) { + return key in objectMeta.cache; } - return camelizeKeys ? underscore(key) : key; - }, + } - init: function() { - this._createReference(); - if (!this._dirtyAttributes) { - set(this, '_dirtyAttributes', Ember.A([])); - } - this._super(); - }, - - _createReference: function() { - var reference = this._reference, - id = this.getPrimaryKey(); - - if (!reference) { - reference = this.constructor._getOrCreateReferenceForId(id); - set(reference, 'record', this); - this._reference = reference; - } else if (reference.id !== id) { - reference.id = id; - this.constructor._cacheReference(reference); + function isDescriptor(value) { + // Ember < 1.11 + if (Ember.Descriptor !== undefined) { + return value instanceof Ember.Descriptor; } + // Ember >= 1.11 + return value && typeof value === 'object' && value.isDescriptor; + } - if (!reference.id) { - reference.id = id; - } + Ember.Model = Ember.Object.extend(Ember.Evented, { + isLoaded: true, + isLoading: Ember.computed.not('isLoaded'), + isNew: true, + isDeleted: false, + _dirtyAttributes: null, + + /** + Called when attribute is accessed. + + @method getAttr + @param key {String} key which is being accessed + @param value {Object} value, which will be returned from getter by default + */ + getAttr: function(key, value) { + return value; + }, - return reference; - }, + toStringExtension() { + return this.id; + }, - getPrimaryKey: function() { - return get(this, get(this.constructor, 'primaryKey')); - }, + isDirty: function() { + var dirtyAttributes = get(this, '_dirtyAttributes'); + return dirtyAttributes && dirtyAttributes.length !== 0 || false; + }.property('_dirtyAttributes.length'), - load: function(id, hash) { - var data = {}; - data[get(this.constructor, 'primaryKey')] = id; - set(this, '_data', Ember.merge(data, hash)); - this.getWithDefault('_dirtyAttributes', Ember.A([])).clear(); + _relationshipBecameDirty: function(name) { + var dirtyAttributes = get(this, '_dirtyAttributes'); + if (dirtyAttributes) { + dirtyAttributes.addObject(name); + } else { + set(this, '_dirtyAttributes', Ember.A([name])); + } + }, - this._reloadHasManys(); + _relationshipBecameClean: function(name) { + var dirtyAttributes = get(this, '_dirtyAttributes'); + if (dirtyAttributes) { dirtyAttributes.removeObject(name); } + }, - // eagerly load embedded data - var relationships = this.constructor._relationships || [], meta = Ember.meta(this), relationshipKey, relationship, relationshipMeta, relationshipData, relationshipType; - for (var i = 0, l = relationships.length; i < l; i++) { - relationshipKey = relationships[i]; - relationshipMeta = this.constructor.metaForProperty(relationshipKey); + dataKey: function(key) { + var camelizeKeys = get(this.constructor, 'camelizeKeys'); + var meta = this.constructor.metaForProperty(key); + if (meta.options && meta.options.key) { + return camelizeKeys ? underscore(meta.options.key) : meta.options.key; + } + return camelizeKeys ? underscore(key) : key; + }, - if (relationshipMeta.options.embedded) { - relationshipType = relationshipMeta.type; - if (typeof relationshipType === "string") { - relationshipType = Ember.get(Ember.lookup, relationshipType) || Ember.getOwner(this).resolveRegistration('model:'+ relationshipType); - } + init: function() { + this._createReference(); + if (!this._dirtyAttributes) { + set(this, '_dirtyAttributes', Ember.A([])); + } + this._super(); + }, + + _createReference: function() { + var reference = this._reference, + id = this.getPrimaryKey(); + + if (!reference) { + reference = this.constructor._getOrCreateReferenceForId(id); + set(reference, 'record', this); + this._reference = reference; + } else if (reference.id !== id) { + reference.id = id; + this.constructor._cacheReference(reference); + } + + if (!reference.id) { + reference.id = id; + } + + return reference; + }, + + getPrimaryKey: function() { + return get(this, get(this.constructor, 'primaryKey')); + }, + + load: function(id, hash) { + var data = {}; + data[get(this.constructor, 'primaryKey')] = id; + data = Object.assign({}, data, hash); + set(this, '_data', data); + this.getWithDefault('_dirtyAttributes', []).clear(); - relationshipData = data[relationshipKey]; - if (relationshipData) { - relationshipType.load(relationshipData); + this._reloadHasManys(); + + // eagerly load embedded data + for (let [relationshipKey, relationshipMeta] of this.constructor.relationships) { + if (relationshipMeta.options.embedded && ! relationshipMeta.options.polymorphic) { + var relationshipType = relationshipMeta.type; + if (typeof relationshipType === "string") { + relationshipType = Ember.get(Ember.lookup, relationshipType) || this.container.lookupFactory('model:'+ relationshipType); + } + + // PATCH(bburley): Use transformed data key for relationships. + var dataKey = this.dataKey(relationshipKey); + relationshipData = data[dataKey]; + var relationshipData = data[relationshipKey]; + if (relationshipData) { + relationshipType.load(relationshipData); + } } } - } - set(this, 'isNew', false); - set(this, 'isLoaded', true); - this._createReference(); - this.trigger('didLoad'); - }, - - didDefineProperty: function(proto, key, value) { - if (isDescriptor(value)) { - var meta = value.meta(); - var klass = proto.constructor; - - if (meta.isAttribute) { - if (!klass._attributes) { klass._attributes = []; } - klass._attributes.push(key); - } else if (meta.isRelationship) { - if (!klass._relationships) { klass._relationships = []; } - klass._relationships.push(key); - meta.relationshipKey = key; + set(this, 'isNew', false); + set(this, 'isLoaded', true); + this._createReference(); + this.trigger('didLoad'); + }, + + + didDefineProperty: function(proto, key, value) { + if (isDescriptor(value)) { + var meta = value.meta(); + var klass = proto.constructor; + + if (meta.isAttribute) { + if (!klass._attributes) { klass._attributes = []; } + klass._attributes.push(key); + } else if (meta.isRelationship) { + if (!klass._relationships) { klass._relationships = []; } + klass._relationships.push(key); + meta.relationshipKey = key; + } } - } - }, + }, - serializeHasMany: function(key, meta) { - return this.get(key).toJSON(); - }, + serializeHasMany: function(key, meta) { + return this.get(key).toJSON(); + }, - serializeBelongsTo: function(key, meta) { - if (meta.options.embedded) { - var record = this.get(key); - return record ? record.toJSON() : null; - } else { - var primaryKey = get(meta.getType(this), 'primaryKey'); - return this.get(key + '.' + primaryKey); - } - }, - - toJSON: function() { - var key, meta, - json = {}, - attributes = this.constructor.getAttributes(), - relationships = this.constructor.getRelationships(), - properties = attributes ? this.getProperties(attributes) : {}, - rootKey = get(this.constructor, 'rootKey'); - - for (key in properties) { - meta = this.constructor.metaForProperty(key); - if (meta.type && meta.type.serialize) { - json[this.dataKey(key)] = meta.type.serialize(properties[key]); - } else if (meta.type && Ember.Model.dataTypes[meta.type]) { - json[this.dataKey(key)] = Ember.Model.dataTypes[meta.type].serialize(properties[key]); + serializeBelongsTo: function(key, meta) { + if (meta.options.embedded) { + var record = this.get(key); + return record ? record.toJSON() : null; } else { - json[this.dataKey(key)] = properties[key]; + var primaryKey = get(meta.getType(this, meta.type), 'primaryKey'); + return this.get(key + '.' + primaryKey); } - } + }, - if (relationships) { - var data, relationshipKey; + toJSON: function() { + var json = {}; + var rootKey = get(this.constructor, 'rootKey'); + + // Using ES5 getters feature here `this[key]` instead of + // this.get(key) + for (let [key, meta] of this.constructor.attributes) { + if (meta.type && meta.type.serialize) { + json[this.dataKey(key)] = meta.type.serialize(this[key]); + } else if (meta.type && Ember.Model.dataTypes[meta.type]) { + json[this.dataKey(key)] = Ember.Model.dataTypes[meta.type].serialize(this[key]); + } else { + json[this.dataKey(key)] = this[key]; + } + } - for(var i = 0; i < relationships.length; i++) { - key = relationships[i]; - meta = this.constructor.metaForProperty(key); - relationshipKey = meta.options.key || key; + for (let [key, meta] of this.constructor.relationships) { + let data; + let relationshipKey = meta.options.key || key; if (meta.kind === 'belongsTo') { data = this.serializeBelongsTo(key, meta); @@ -732,1418 +733,1458 @@ Ember.Model = Ember.Object.extend(Ember.Evented, { json[relationshipKey] = data; } - } - if (rootKey) { - var jsonRoot = {}; - jsonRoot[rootKey] = json; - return jsonRoot; - } else { - return json; - } - }, - - save: function() { - var adapter = this.constructor.adapter; - set(this, 'isSaving', true); - if (get(this, 'isNew')) { - return adapter.createRecord(this); - } else if (get(this, 'isDirty')) { - return adapter.saveRecord(this); - } else { // noop, return a resolved promise - var self = this, - promise = new Ember.RSVP.Promise(function(resolve, reject) { - resolve(self); - }); - set(this, 'isSaving', false); - return promise; - } - }, - - reload: function() { - this.getWithDefault('_dirtyAttributes', Ember.A([])).clear(); - return this.constructor.reload(this.get(get(this.constructor, 'primaryKey')), Ember.getOwner(this)); - }, - - revert: function() { - this.getWithDefault('_dirtyAttributes', Ember.A([])).clear(); - this.notifyPropertyChange('_data'); - this._reloadHasManys(true); - }, - - didCreateRecord: function() { - var primaryKey = get(this.constructor, 'primaryKey'), - id = get(this, primaryKey); - - set(this, 'isNew', false); - - set(this, '_dirtyAttributes', Ember.A([])); - this.constructor.addToRecordArrays(this); - this.trigger('didCreateRecord'); - this.didSaveRecord(); - }, - - didSaveRecord: function() { - set(this, 'isSaving', false); - this.trigger('didSaveRecord'); - if (this.get('isDirty')) { this._copyDirtyAttributesToData(); } - }, - - deleteRecord: function() { - return this.constructor.adapter.deleteRecord(this); - }, - - didDeleteRecord: function() { - this.constructor.removeFromRecordArrays(this); - set(this, 'isDeleted', true); - this.trigger('didDeleteRecord'); - }, - - _copyDirtyAttributesToData: function() { - if (!this._dirtyAttributes) { return; } - var dirtyAttributes = this._dirtyAttributes, - data = get(this, '_data'), - key; - - if (!data) { - data = {}; - set(this, '_data', data); - } - for (var i = 0, l = dirtyAttributes.length; i < l; i++) { - // TODO: merge Object.create'd object into prototype - key = dirtyAttributes[i]; - data[this.dataKey(key)] = this.cacheFor(key); - } - set(this, '_dirtyAttributes', Ember.A([])); - this._resetDirtyStateInNestedObjects(this); // we need to reset isDirty state to all child objects in embedded relationships - }, - - _resetDirtyStateInNestedObjects: function(object) { - var i, obj; - if (object._hasManyArrays) { - for (i = 0; i < object._hasManyArrays.length; i++) { - var array = object._hasManyArrays[i]; - array.revert(); - if (array.embedded) { - for (var j = 0; j < array.get('length'); j++) { - obj = array.objectAt(j); - obj._copyDirtyAttributesToData(); - } - } + if (rootKey) { + var jsonRoot = {}; + jsonRoot[rootKey] = json; + return jsonRoot; + } else { + return json; } - } + }, - if (object._belongsTo) { - for (i = 0; i < object._belongsTo.length; i++) { - var belongsTo = object._belongsTo[i]; - if (belongsTo.options.embedded) { - obj = this.get(belongsTo.relationshipKey); - if (obj) { - obj._copyDirtyAttributesToData(); - } - } + save: function() { + var adapter = this.constructor.adapter; + set(this, 'isSaving', true); + if (get(this, 'isNew')) { + return adapter.createRecord(this); + } else if (get(this, 'isDirty')) { + return adapter.saveRecord(this); + } else { // noop, return a resolved promise + var self = this, + promise = new Ember.RSVP.Promise(function(resolve, reject) { + resolve(self); + }); + set(this, 'isSaving', false); + return promise; } - } - }, + }, + + reload: function() { + this.getWithDefault('_dirtyAttributes', Ember.A([])).clear(); + return this.constructor.reload(this.get(get(this.constructor, 'primaryKey')), this.container); + }, + + revert: function() { + this.getWithDefault('_dirtyAttributes', Ember.A([])).clear(); + this.notifyPropertyChange('_data'); + this._reloadHasManys(true); + + // PATCH(b marie) + // dirty attributes were getting added with nested models sometime after calling reloadHasManys + // this started happening in Ember 2.1 + this.getWithDefault('_dirtyAttributes', Ember.A([])).clear(); + }, - _registerHasManyArray: function(array) { - if (!this._hasManyArrays) { this._hasManyArrays = Ember.A([]); } + didCreateRecord: function() { + var primaryKey = get(this.constructor, 'primaryKey'), + id = get(this, primaryKey); - this._hasManyArrays.pushObject(array); - }, + set(this, 'isNew', false); + + set(this, '_dirtyAttributes', Ember.A([])); + this.constructor.addToRecordArrays(this); + this.trigger('didCreateRecord'); + this.didSaveRecord(); + }, - registerParentHasManyArray: function(array) { - if (!this._parentHasManyArrays) { this._parentHasManyArrays = Ember.A([]); } + didSaveRecord: function() { + set(this, 'isSaving', false); + this.trigger('didSaveRecord'); + if (this.get('isDirty')) { this._copyDirtyAttributesToData(); } + }, - this._parentHasManyArrays.pushObject(array); - }, + deleteRecord: function() { + return this.constructor.adapter.deleteRecord(this); + }, - unregisterParentHasManyArray: function(array) { - if (!this._parentHasManyArrays) { return; } + didDeleteRecord: function() { + this.constructor.removeFromRecordArrays(this); + set(this, 'isDeleted', true); + this.trigger('didDeleteRecord'); + }, - this._parentHasManyArrays.removeObject(array); - }, + _copyDirtyAttributesToData: function() { + if (!this._dirtyAttributes) { return; } + var dirtyAttributes = this._dirtyAttributes, + data = get(this, '_data'), + key; - _reloadHasManys: function(reverting) { - if (!this._hasManyArrays) { return; } - var i, j; - for (i = 0; i < this._hasManyArrays.length; i++) { - var array = this._hasManyArrays[i], - hasManyContent = this._getHasManyContent(get(array, 'key'), get(array, 'modelClass'), get(array, 'embedded')); - if (!reverting) { - for (j = 0; j < array.get('length'); j++) { - if (array.objectAt(j).get('isNew') && !array.objectAt(j).get('isDeleted')) { - hasManyContent.addObject(array.objectAt(j)._reference); + if (!data) { + data = {}; + set(this, '_data', data); + } + for (var i = 0, l = dirtyAttributes.length; i < l; i++) { + // TODO: merge Object.create'd object into prototype + key = dirtyAttributes[i]; + data[this.dataKey(key)] = this.cacheFor(key); + } + set(this, '_dirtyAttributes', Ember.A([])); + this._resetDirtyStateInNestedObjects(this); // we need to reset isDirty state to all child objects in embedded relationships + }, + + _resetDirtyStateInNestedObjects: function(object) { + var i, obj; + if (object._hasManyArrays) { + for (i = 0; i < object._hasManyArrays.length; i++) { + var array = object._hasManyArrays[i]; + array.revert(); + if (array.embedded) { + for (var j = 0; j < array.get('length'); j++) { + obj = array.objectAt(j); + obj._copyDirtyAttributesToData(); + } } } } - array.load(hasManyContent); - } - }, - - _getHasManyContent: function(key, type, embedded) { - var content = get(this, '_data.' + key); - - if (content) { - var mapFunction, primaryKey, reference; - if (embedded) { - primaryKey = get(type, 'primaryKey'); - mapFunction = function(attrs) { - reference = type._getOrCreateReferenceForId(attrs[primaryKey]); - reference.data = attrs; - return reference; - }; - } else { - mapFunction = function(id) { return type._getOrCreateReferenceForId(id); }; - } - content = content.map(mapFunction); - } - return Ember.A(content || []); - }, + if (object._belongsTo) { + for (i = 0; i < object._belongsTo.length; i++) { + var belongsTo = object._belongsTo[i]; + if (belongsTo.options.embedded) { + obj = this.get(belongsTo.relationshipKey); + if (obj) { + obj._copyDirtyAttributesToData(); + } + } + } + } + }, - _registerBelongsTo: function(key) { - if (!this._belongsTo) { this._belongsTo = Ember.A([]); } + _registerHasManyArray: function(array) { + if (!this._hasManyArrays) { this._hasManyArrays = Ember.A([]); } - this._belongsTo.pushObject(key); - } -}); + this._hasManyArrays.pushObject(array); + }, -Ember.Model.reopenClass({ - primaryKey: 'id', + registerParentHasManyArray: function(array) { + if (!this._parentHasManyArrays) { this._parentHasManyArrays = Ember.A([]); } - adapter: Ember.Adapter.create(), + this._parentHasManyArrays.pushObject(array); + }, - _clientIdCounter: 1, + unregisterParentHasManyArray: function(array) { + if (!this._parentHasManyArrays) { return; } - getAttributes: function() { - this.proto(); // force class "compilation" if it hasn't been done. - var attributes = this._attributes || []; - if (typeof this.superclass.getAttributes === 'function') { - attributes = this.superclass.getAttributes().concat(attributes); - } - return attributes; - }, - - getRelationships: function() { - this.proto(); // force class "compilation" if it hasn't been done. - var relationships = this._relationships || []; - if (typeof this.superclass.getRelationships === 'function') { - relationships = this.superclass.getRelationships().concat(relationships); - } - return relationships; - }, + this._parentHasManyArrays.removeObject(array); + }, - fetch: function(id) { - if (!arguments.length) { - return this._findFetchAll(true); - } else if (Ember.isArray(id)) { - return this._findFetchMany(id, true); - } else if (typeof id === 'object') { - return this._findFetchQuery(id, true); - } else { - return this._findFetchById(id, true); - } - }, + _reloadHasManys: function(reverting) { + if (!this._hasManyArrays) { return; } + var i, j; + for (i = 0; i < this._hasManyArrays.length; i++) { + var array = this._hasManyArrays[i], + hasManyContent = this._getHasManyContent(get(array, 'key'), get(array, 'modelClass'), get(array, 'embedded')); + if (!reverting) { + for (j = 0; j < array.get('length'); j++) { + // PATCH(johnweathers): In some cases, a hasMany array has had an object removed + // from it, but its cached length has not been updated yet and in such cases + // we might have an invalid index value for 'j' here. So we need to add an initial + // check to this conditional to make sure that we have an object at location j before + // trying to operate on it + if (array.objectAt(j) && array.objectAt(j).get('isNew') && !array.objectAt(j).get('isDeleted')) { + // PATCH(mpa): Sometimes when reloading data received from the server for a freshly created + // object, a stale cached reference to the original unsaved object is still in the hasManyContent + // and yet our new fully loaded copy of the object has a difference reference and so if we + // do not first remove the stale copy before adding the loaded copy, we will have two copies + // in memory and see a client-side only duplicate of this object + var ref, existing; + hasManyContent.addObject(array.objectAt(j)._reference); + ref = array.objectAt(j)._reference; + if (ref.record && ref.record.id) { + existing = hasManyContent.findBy('id', ref.record.id); + if (existing) { + hasManyContent.removeObject(existing); + } + } + hasManyContent.addObject(ref); + } + } + } + array.load(hasManyContent); + } + }, - find: function(id) { - if (!arguments.length) { - return this._findFetchAll(false); - } else if (Ember.isArray(id)) { - return this._findFetchMany(id, false); - } else if (typeof id === 'object') { - return this._findFetchQuery(id, false); - } else { - return this._findFetchById(id, false); - } - }, + _getHasManyContent: function(key, type, embedded) { + var content = get(this, '_data.' + key); + + if (content) { + var mapFunction, primaryKey, reference; + if (embedded) { + primaryKey = get(type, 'primaryKey'); + mapFunction = function(attrs) { + // PATCH(mattwright): The primary key may be a computed property here. + // This seems to be happening for the ItemModifierListAggregated model + // at least. Using `Ember.get` ensures we're get the value and not the + // `computed` function. The better fix would be to prevent this from + // happening in the first place, but after hours of trying I can't + // figure out where this is happening. + // + // TODO(mattwright): Attempt to rip this out once we've moved past + // Ember 1.13 and/or refactored or (ideally) removed the hella complex + // COGS/multiunit/aggregate item code. + reference = type._getOrCreateReferenceForId(Ember.get(attrs, primaryKey)); + reference.data = attrs; + return reference; + }; + } else { + mapFunction = function(id) { return type._getOrCreateReferenceForId(id); }; + } + content = content.map(mapFunction); + } - findQuery: function(params) { - return this._findFetchQuery(params, false); - }, + return Ember.A(content || []); + }, - fetchQuery: function(params) { - return this._findFetchQuery(params, true); - }, + _registerBelongsTo: function(key) { + if (!this._belongsTo) { this._belongsTo = Ember.A([]); } - _findFetchQuery: function(params, isFetch, owner) { - var records = Ember.RecordArray.create({modelClass: this, _query: params}); - Ember.setOwner(records, owner); + this._belongsTo.pushObject(key); + } + }); - var promise = this.adapter.findQuery(this, records, params); + Ember.Model.reopenClass({ + primaryKey: 'id', - return isFetch ? promise : records; - }, + adapter: Ember.Adapter.create(), - findMany: function(ids) { - return this._findFetchMany(ids, false); - }, + _clientIdCounter: 1, - fetchMany: function(ids) { - return this._findFetchMany(ids, true); - }, + fetch: function(id) { + if (!arguments.length) { + return this._findFetchAll(true); + } else if (Ember.isArray(id)) { + return this._findFetchMany(id, true); + } else if (typeof id === 'object') { + return this._findFetchQuery(id, true); + } else { + return this._findFetchById(id, true); + } + }, - _findFetchMany: function(ids, isFetch, owner) { - Ember.assert("findFetchMany requires an array", Ember.isArray(ids)); + find: function(id) { + if (!arguments.length) { + return this._findFetchAll(false); + } else if (Ember.isArray(id)) { + return this._findFetchMany(id, false); + } else if (typeof id === 'object') { + return this._findFetchQuery(id, false); + } else { + return this._findFetchById(id, false); + } + }, - var records = Ember.RecordArray.create({_ids: ids, modelClass: this}), - deferred; + findQuery: function(params) { + return this._findFetchQuery(params, false); + }, - Ember.setOwner(records, owner); + fetchQuery: function(params) { + return this._findFetchQuery(params, true); + }, - if (!this.recordArrays) { this.recordArrays = []; } - this.recordArrays.push(records); + _findFetchQuery: function(params, isFetch, container) { + var records = Ember.RecordArray.create({modelClass: this, _query: params, container: container}); - if (this._currentBatchIds) { - concatUnique(this._currentBatchIds, ids); - this._currentBatchRecordArrays.push(records); - } else { - this._currentBatchIds = concatUnique([], ids); - this._currentBatchRecordArrays = [records]; - } + var promise = this.adapter.findQuery(this, records, params); - if (isFetch) { - deferred = Ember.RSVP.defer(); - Ember.set(deferred, 'resolveWith', records); + return isFetch ? promise : records; + }, - if (!this._currentBatchDeferreds) { this._currentBatchDeferreds = []; } - this._currentBatchDeferreds.push(deferred); - } + findMany: function(ids) { + return this._findFetchMany(ids, false); + }, - Ember.run.scheduleOnce('data', this, this._executeBatch, owner); + fetchMany: function(ids) { + return this._findFetchMany(ids, true); + }, - return isFetch ? deferred.promise : records; - }, + _findFetchMany: function(ids, isFetch, container) { + Ember.assert("findFetchMany requires an array", Ember.isArray(ids)); - findAll: function() { - return this._findFetchAll(false); - }, + var records = Ember.RecordArray.create({_ids: ids, modelClass: this, container: container}), + deferred; - fetchAll: function() { - return this._findFetchAll(true); - }, + if (!this.recordArrays) { this.recordArrays = []; } + this.recordArrays.push(records); - _findFetchAll: function(isFetch, owner) { - var self = this; + if (this._currentBatchIds) { + concatUnique(this._currentBatchIds, ids); + this._currentBatchRecordArrays.push(records); + } else { + this._currentBatchIds = concatUnique([], ids); + this._currentBatchRecordArrays = [records]; + } - var currentFetchPromise = this._currentFindFetchAllPromise; - if (isFetch && currentFetchPromise) { - return currentFetchPromise; - } else if (this._findAllRecordArray) { if (isFetch) { - return new Ember.RSVP.Promise(function(resolve) { - resolve(self._findAllRecordArray); - }); - } else { - return this._findAllRecordArray; + deferred = Ember.RSVP.defer(); + Ember.set(deferred, 'resolveWith', records); + + if (!this._currentBatchDeferreds) { this._currentBatchDeferreds = []; } + this._currentBatchDeferreds.push(deferred); } - } - var records = this._findAllRecordArray = Ember.RecordArray.create({modelClass: this}); - Ember.setOwner(records, owner); + Ember.run.scheduleOnce('data', this, this._executeBatch, container); - var promise = this._currentFindFetchAllPromise = this.adapter.findAll(this, records); + return isFetch ? deferred.promise : records; + }, - promise['finally'](function() { - self._currentFindFetchAllPromise = null; - }); + findAll: function() { + return this._findFetchAll(false); + }, - // Remove the cached record array if the promise is rejected - if (promise.then) { - promise.then(null, function() { - self._findAllRecordArray = null; - return Ember.RSVP.reject.apply(null, arguments); - }); - } + fetchAll: function() { + return this._findFetchAll(true); + }, - return isFetch ? promise : records; - }, + _findFetchAll: function(isFetch, container) { + var self = this; - findById: function(id) { - return this._findFetchById(id, false); - }, + var currentFetchPromise = this._currentFindFetchAllPromise; + if (isFetch && currentFetchPromise) { + return currentFetchPromise; + } else if (this._findAllRecordArray) { + if (isFetch) { + return new Ember.RSVP.Promise(function(resolve) { + resolve(self._findAllRecordArray); + }); + } else { + return this._findAllRecordArray; + } + } - fetchById: function(id) { - return this._findFetchById(id, true); - }, + var records = this._findAllRecordArray = Ember.RecordArray.create({modelClass: this, container: container}); - _findFetchById: function(id, isFetch, owner) { - var record = this.cachedRecordForId(id, owner), - isLoaded = get(record, 'isLoaded'), - adapter = get(this, 'adapter'), - deferredOrPromise; + var promise = this._currentFindFetchAllPromise = this.adapter.findAll(this, records); - if (isLoaded) { - if (isFetch) { - return new Ember.RSVP.Promise(function(resolve, reject) { - resolve(record); + promise['finally'](function() { + self._currentFindFetchAllPromise = null; + }); + + // Remove the cached record array if the promise is rejected + if (promise.then) { + promise.then(null, function() { + self._findAllRecordArray = null; + return Ember.RSVP.reject.apply(null, arguments); }); - } else { - return record; } - } - deferredOrPromise = this._fetchById(record, id); - - return isFetch ? deferredOrPromise : record; - }, + return isFetch ? promise : records; + }, - _currentBatchIds: null, - _currentBatchRecordArrays: null, - _currentBatchDeferreds: null, + findById: function(id) { + return this._findFetchById(id, false); + }, - reload: function(id, owner) { - var record = this.cachedRecordForId(id, owner); - record.set('isLoaded', false); - return this._fetchById(record, id); - }, + fetchById: function(id) { + return this._findFetchById(id, true); + }, - _fetchById: function(record, id) { - var adapter = get(this, 'adapter'), - deferred; + _findFetchById: function(id, isFetch, container) { + var record = this.cachedRecordForId(id, container), + isLoaded = get(record, 'isLoaded'), + adapter = get(this, 'adapter'), + deferredOrPromise; - if (adapter.findMany && !adapter.findMany.isUnimplemented) { - if (this._currentBatchIds) { - if (!contains(this._currentBatchIds, id)) { this._currentBatchIds.push(id); } - } else { - this._currentBatchIds = [id]; - this._currentBatchRecordArrays = []; + if (isLoaded) { + if (isFetch) { + return new Ember.RSVP.Promise(function(resolve, reject) { + resolve(record); + }); + } else { + return record; + } } - deferred = Ember.RSVP.defer(); + deferredOrPromise = this._fetchById(record, id); - //Attached the record to the deferred so we can resolve it later. - Ember.set(deferred, 'resolveWith', record); + return isFetch ? deferredOrPromise : record; + }, - if (!this._currentBatchDeferreds) { this._currentBatchDeferreds = []; } - this._currentBatchDeferreds.push(deferred); + _currentBatchIds: null, + _currentBatchRecordArrays: null, + _currentBatchDeferreds: null, - var owner = Ember.getOwner(record); - Ember.run.scheduleOnce('data', this, this._executeBatch, owner); + reload: function(id, container) { + var record = this.cachedRecordForId(id, container); + record.set('isLoaded', false); + return this._fetchById(record, id); + }, - return deferred.promise; - } else { - return adapter.find(record, id); - } - }, + _fetchById: function(record, id) { + var adapter = get(this, 'adapter'), + deferred; - _executeBatch: function(owner) { - var batchIds = this._currentBatchIds, - batchRecordArrays = this._currentBatchRecordArrays, - batchDeferreds = this._currentBatchDeferreds, - self = this, - requestIds = [], - promise, - i; + if (adapter.findMany && !adapter.findMany.isUnimplemented) { + if (this._currentBatchIds) { + if (!contains(this._currentBatchIds, id)) { this._currentBatchIds.push(id); } + } else { + this._currentBatchIds = [id]; + this._currentBatchRecordArrays = []; + } - this._currentBatchIds = null; - this._currentBatchRecordArrays = null; - this._currentBatchDeferreds = null; + deferred = Ember.RSVP.defer(); - for (i = 0; i < batchIds.length; i++) { - if (!this.cachedRecordForId(batchIds[i], owner).get('isLoaded')) { - requestIds.push(batchIds[i]); - } - } + //Attached the record to the deferred so we can resolve it later. + Ember.set(deferred, 'resolveWith', record); - if (requestIds.length === 1) { - promise = get(this, 'adapter').find(this.cachedRecordForId(requestIds[0], owner), requestIds[0]); - } else { - var recordArray = Ember.RecordArray.create({_ids: batchIds }); - Ember.setOwner(recordArray, owner); - if (requestIds.length === 0) { - promise = new Ember.RSVP.Promise(function(resolve, reject) { resolve(recordArray); }); - recordArray.notifyLoaded(); - } else { - promise = get(this, 'adapter').findMany(this, recordArray, requestIds); - } - } + if (!this._currentBatchDeferreds) { this._currentBatchDeferreds = []; } + this._currentBatchDeferreds.push(deferred); - promise.then(function() { - for (var i = 0, l = batchRecordArrays.length; i < l; i++) { - batchRecordArrays[i].loadForFindMany(self); + Ember.run.scheduleOnce('data', this, this._executeBatch, record.container); + + return deferred.promise; + } else { + return adapter.find(record, id); } + }, - if (batchDeferreds) { - for (i = 0, l = batchDeferreds.length; i < l; i++) { - var resolveWith = Ember.get(batchDeferreds[i], 'resolveWith'); - batchDeferreds[i].resolve(resolveWith); + _executeBatch: function(container) { + var batchIds = this._currentBatchIds, + batchRecordArrays = this._currentBatchRecordArrays, + batchDeferreds = this._currentBatchDeferreds, + self = this, + requestIds = [], + promise, + i; + + this._currentBatchIds = null; + this._currentBatchRecordArrays = null; + this._currentBatchDeferreds = null; + + for (i = 0; i < batchIds.length; i++) { + if (!this.cachedRecordForId(batchIds[i]).get('isLoaded')) { + requestIds.push(batchIds[i]); } } - }).then(null, function(errorXHR) { - if (batchDeferreds) { - for (var i = 0, l = batchDeferreds.length; i < l; i++) { - batchDeferreds[i].reject(errorXHR); + + if (requestIds.length === 1) { + promise = get(this, 'adapter').find(this.cachedRecordForId(requestIds[0], container), requestIds[0]); + } else { + var recordArray = Ember.RecordArray.create({_ids: batchIds, container: container}); + if (requestIds.length === 0) { + promise = new Ember.RSVP.Promise(function(resolve, reject) { resolve(recordArray); }); + recordArray.notifyLoaded(); + } else { + promise = get(this, 'adapter').findMany(this, recordArray, requestIds); } } - }); - }, - - getCachedReferenceRecord: function(id){ - var ref = this._getReferenceById(id); - if(ref && ref.record) { - return ref.record; - } - return undefined; - }, - cachedRecordForId: function(id, owner) { - var record; - if (!this.transient) { - record = this.getCachedReferenceRecord(id); - } + promise.then(function() { + for (var i = 0, l = batchRecordArrays.length; i < l; i++) { + batchRecordArrays[i].loadForFindMany(self); + } - if (!record) { - var primaryKey = get(this, 'primaryKey'), - attrs = {isLoaded: false}; + if (batchDeferreds) { + for (i = 0, l = batchDeferreds.length; i < l; i++) { + var resolveWith = Ember.get(batchDeferreds[i], 'resolveWith'); + batchDeferreds[i].resolve(resolveWith); + } + } + }).then(null, function(errorXHR) { + if (batchDeferreds) { + for (var i = 0, l = batchDeferreds.length; i < l; i++) { + batchDeferreds[i].reject(errorXHR); + } + } + }); + }, - attrs[primaryKey] = id; - record = this.create(attrs); - Ember.setOwner(record, owner); + getCachedReferenceRecord: function(id, container){ + var ref = this._getReferenceById(id); + if(ref && ref.record) { + if (! ref.record.container) { + ref.record.container = container; + } + return ref.record; + } + return undefined; + }, + cachedRecordForId: function(id, container) { + var record; if (!this.transient) { - var sideloadedData = this.sideloadedData && this.sideloadedData[id]; - if (sideloadedData) { - record.load(id, sideloadedData); + record = this.getCachedReferenceRecord(id, container); + } + + if (!record) { + var primaryKey = get(this, 'primaryKey'), + attrs = {isLoaded: false}; + + attrs[primaryKey] = id; + attrs.container = container; + record = this.create(attrs); + if (!this.transient) { + var sideloadedData = this.sideloadedData && this.sideloadedData[id]; + if (sideloadedData) { + record.load(id, sideloadedData); + } } } - } - return record; - }, + return record; + }, + attributes: Ember.computed(function() { + let map = new Map(); - addToRecordArrays: function(record) { - if (this._findAllRecordArray) { - this._findAllRecordArray.addObject(record); - } - if (this.recordArrays) { - this.recordArrays.forEach(function(recordArray) { - if (recordArray instanceof Ember.FilteredRecordArray) { - recordArray.registerObserversOnRecord(record); - recordArray.updateFilter(); - } else { - recordArray.addObject(record); + this.eachComputedProperty((name, meta) => { + if (meta.isAttribute) { + meta.name = name; + map.set(name, meta); } }); - } - }, - - unload: function (record) { - this.removeFromHasManyArrays(record); - this.removeFromRecordArrays(record); - var primaryKey = record.get(get(this, 'primaryKey')); - this.removeFromCache(primaryKey); - }, - - clearCache: function () { - this.sideloadedData = undefined; - this._referenceCache = undefined; - this._findAllRecordArray = undefined; - }, - - removeFromCache: function (key) { - if (this.sideloadedData && this.sideloadedData[key]) { - delete this.sideloadedData[key]; - } - if(this._referenceCache && this._referenceCache[key]) { - delete this._referenceCache[key]; - } - }, + return map; + }).readOnly(), - removeFromHasManyArrays: function(record) { - if (record._parentHasManyArrays) { - record._parentHasManyArrays.forEach(function(hasManyArray) { - hasManyArray.unloadObject(record); - }); - record._parentHasManyArrays = null; - } - }, + relationships: Ember.computed(function() { + let map = new Map(); - removeFromRecordArrays: function(record) { - if (this._findAllRecordArray) { - this._findAllRecordArray.removeObject(record); - } - if (this.recordArrays) { - this.recordArrays.forEach(function(recordArray) { - recordArray.removeObject(record); + this.eachComputedProperty((name, meta) => { + if (meta.isRelationship) { + meta.name = name; + map.set(name, meta); + } }); - } - }, + return map; + }).readOnly(), - // FIXME - findFromCacheOrLoad: function(data, owner) { - var record; - if (!data[get(this, 'primaryKey')]) { - record = this.create({isLoaded: false}); - } else { - record = this.cachedRecordForId(data[get(this, 'primaryKey')], owner); - } - record.load(data[get(this, 'primaryKey')], data); - Ember.setOwner(record, owner); - return record; - }, - - registerRecordArray: function(recordArray) { - if (!this.recordArrays) { this.recordArrays = []; } - this.recordArrays.push(recordArray); - }, + addToRecordArrays: function(record) { + if (this._findAllRecordArray) { + this._findAllRecordArray.addObject(record); + } + if (this.recordArrays) { + this.recordArrays.forEach(function(recordArray) { + if (recordArray instanceof Ember.FilteredRecordArray) { + recordArray.registerObserversOnRecord(record); + recordArray.updateFilter(); + } else { + recordArray.addObject(record); + } + }); + } + }, - unregisterRecordArray: function(recordArray) { - if (!this.recordArrays) { return; } - Ember.A(this.recordArrays).removeObject(recordArray); - }, + unload: function (record) { + this.removeFromHasManyArrays(record); + this.removeFromRecordArrays(record); + var primaryKey = record.get(get(this, 'primaryKey')); + this.removeFromCache(primaryKey); + }, - forEachCachedRecord: function(callback) { - if (!this._referenceCache) { return; } - var ids = Object.keys(this._referenceCache); - ids.map(function(id) { - return this._getReferenceById(id).record; - }, this).forEach(callback); - }, + clearCache: function () { + this.sideloadedData = undefined; + this._referenceCache = undefined; + this._findAllRecordArray = undefined; + }, - load: function(hashes) { - if (Ember.typeOf(hashes) !== 'array') { hashes = [hashes]; } + removeFromCache: function (key) { + if (this.sideloadedData && this.sideloadedData[key]) { + delete this.sideloadedData[key]; + } + if(this._referenceCache && this._referenceCache[key]) { + delete this._referenceCache[key]; + } + }, - if (!this.sideloadedData) { this.sideloadedData = {}; } + removeFromHasManyArrays: function(record) { + if (record._parentHasManyArrays) { + record._parentHasManyArrays.forEach(function(hasManyArray) { + hasManyArray.unloadObject(record); + }); + record._parentHasManyArrays = null; + } + }, - for (var i = 0, l = hashes.length; i < l; i++) { - var hash = hashes[i], - primaryKey = hash[get(this, 'primaryKey')], - record = this.getCachedReferenceRecord(primaryKey); + removeFromRecordArrays: function(record) { + if (this._findAllRecordArray) { + this._findAllRecordArray.removeObject(record); + } + if (this.recordArrays) { + this.recordArrays.forEach(function(recordArray) { + recordArray.removeObject(record); + }); + } + }, - if (record) { - record.load(primaryKey, hash); + // FIXME + findFromCacheOrLoad: function(data, container) { + var record; + if (!data[get(this, 'primaryKey')]) { + record = this.create({isLoaded: false, container: container}); } else { - this.sideloadedData[primaryKey] = hash; + record = this.cachedRecordForId(data[get(this, 'primaryKey')], container); } - } - }, + // set(record, 'data', data); + record.load(data[get(this, 'primaryKey')], data); + return record; + }, - _getReferenceById: function(id) { - if (!this._referenceCache) { this._referenceCache = {}; } - return this._referenceCache[id]; - }, + registerRecordArray: function(recordArray) { + if (!this.recordArrays) { this.recordArrays = []; } + this.recordArrays.push(recordArray); + }, - _getOrCreateReferenceForId: function(id) { - var reference = this._getReferenceById(id); + unregisterRecordArray: function(recordArray) { + if (!this.recordArrays) { return; } + Ember.A(this.recordArrays).removeObject(recordArray); + }, - if (!reference) { - reference = this._createReference(id); - } + forEachCachedRecord: function(callback) { + if (!this._referenceCache) { return; } + var ids = Object.keys(this._referenceCache); + ids.map(function(id) { + return this._getReferenceById(id).record; + }, this).forEach(callback); + }, - return reference; - }, + load: function(hashes, container) { + if (Ember.typeOf(hashes) !== 'array') { hashes = [hashes]; } - _createReference: function(id) { - if (!this._referenceCache) { this._referenceCache = {}; } + if (!this.sideloadedData) { this.sideloadedData = {}; } - Ember.assert('The id ' + id + ' has already been used with another record of type ' + this.toString() + '.', !id || !this._referenceCache[id]); + for (var i = 0, l = hashes.length; i < l; i++) { + var hash = hashes[i]; + var primaryKey = hash[get(this, 'primaryKey')]; + var record = this.getCachedReferenceRecord(primaryKey, container); - var reference = { - id: id, - clientId: this._clientIdCounter++ - }; + if (record) { + record.load(primaryKey, hash); + } else { + this.sideloadedData[primaryKey] = hash; + } + } + }, - this._cacheReference(reference); + _getReferenceById: function(id) { + if (!this._referenceCache) { this._referenceCache = {}; } + return this._referenceCache[id]; + }, - return reference; - }, + _getOrCreateReferenceForId: function(id) { + var reference = this._getReferenceById(id); - _cacheReference: function(reference) { - if (!this._referenceCache) { this._referenceCache = {}; } + if (!reference) { + reference = this._createReference(id); + } - // if we're creating an item, this process will be done - // later, once the object has been persisted. - if (!Ember.isEmpty(reference.id)) { - this._referenceCache[reference.id] = reference; - } - } -}); + return reference; + }, + _createReference: function(id) { + if (!this._referenceCache) { this._referenceCache = {}; } -})(); + Ember.assert('The id ' + id + ' has already been used with another record of type ' + this.toString() + '.', !id || !this._referenceCache[id]); -(function() { + var reference = { + id: id, + clientId: this._clientIdCounter++ + }; -var supportsComputedGetterSetter; + this._cacheReference(reference); -try { - Ember.computed({ - get: function() { }, - set: function() { } + return reference; + }, + + toString() { + return `Ember.Model`; + }, + + _cacheReference: function(reference) { + if (!this._referenceCache) { this._referenceCache = {}; } + + // if we're creating an item, this process will be done + // later, once the object has been persisted. + if (!Ember.isEmpty(reference.id)) { + this._referenceCache[reference.id] = reference; + } + } }); - supportsComputedGetterSetter = true; -} catch(e) { - supportsComputedGetterSetter = false; -} -Ember.Model.computed = function() { - var polyfillArguments = []; - var config = arguments[arguments.length - 1]; - if (typeof config === 'function' || supportsComputedGetterSetter) { - return Ember.computed.apply(null, arguments); - } + })(); + + (function() { + + var supportsComputedGetterSetter; + + try { + Ember.computed({ + get: function() { }, + set: function() { } + }); + supportsComputedGetterSetter = true; + } catch(e) { + supportsComputedGetterSetter = false; + } + + Ember.Model.computed = function() { + var polyfillArguments = []; + var config = arguments[arguments.length - 1]; + + if (typeof config === 'function' || supportsComputedGetterSetter) { + return Ember.computed.apply(null, arguments); + } - for (var i = 0, l = arguments.length - 1; i < l; i++) { - polyfillArguments.push(arguments[i]); - } + for (var i = 0, l = arguments.length - 1; i < l; i++) { + polyfillArguments.push(arguments[i]); + } - var func; - if (config.set) { - func = function(key, value, oldValue) { - if (arguments.length > 1) { - return config.set.call(this, key, value, oldValue); - } else { + var func; + if (config.set) { + func = function(key, value, oldValue) { + if (arguments.length > 1) { + return config.set.call(this, key, value, oldValue); + } else { + return config.get.call(this, key); + } + }; + } else { + func = function(key) { return config.get.call(this, key); - } - }; - } else { - func = function(key) { - return config.get.call(this, key); - }; - } + }; + } - polyfillArguments.push(func); + polyfillArguments.push(func); - return Ember.computed.apply(null, polyfillArguments); -}; + return Ember.computed.apply(null, polyfillArguments); + }; -})(); + })(); -(function() { + (function() { -var get = Ember.get; + var get = Ember.get; -function getType(record) { - var type = this.type; + function getType(record) { + var type = this.type; - if (typeof this.type === "string" && this.type) { - this.type = get(Ember.lookup, this.type); + if (typeof this.type === "string" && this.type) { + this.type = get(Ember.lookup, this.type); - if (!this.type) { - var emstore = Ember.getOwner(record).lookup('emstore:main'); - this.type = emstore.modelFor(type); - this.type.reopenClass({ adapter: emstore.adapterFor(type) }); + if (!this.type) { + var store = record.container.lookup('store:main'); + this.type = store.modelFor(type); + this.type.reopenClass({ adapter: store.adapterFor(type) }); + } } - } - return this.type; -} + return this.type; + } -Ember.hasMany = function(type, options) { - options = options || {}; + Ember.hasMany = function(type, options) { + options = options || {}; - var meta = { type: type, isRelationship: true, options: options, kind: 'hasMany', getType: getType}; + var meta = { type: type, isRelationship: true, options: options, kind: 'hasMany', getType: getType}; - return Ember.Model.computed({ - get: function(propertyKey) { - type = meta.getType(this); - Ember.assert("Type cannot be empty", !Ember.isEmpty(type)); + return Ember.Model.computed({ + get: function(propertyKey) { + type = meta.getType(this); + Ember.assert("Type cannot be empty", !Ember.isEmpty(type)); - var key = options.key || propertyKey; - var owner = Ember.getOwner(this); - return this.getHasMany(key, type, meta, owner); - }, - set: function(propertyKey, newContentArray, existingArray) { - if (!existingArray) { - existingArray = this.getHasMany(options.key || propertyKey, type, meta, Ember.getOwner(this)); + var key = options.key || propertyKey; + return this.getHasMany(key, type, meta, this.container); + }, + set: function(propertyKey, newContentArray, existingArray) { + if (!existingArray) { + existingArray = this.getHasMany(options.key || propertyKey, type, meta, this.container); + } + return existingArray.setObjects(newContentArray); } - return existingArray.setObjects(newContentArray); - } - }).meta(meta); -}; - -Ember.Model.reopen({ - getHasMany: function(key, type, meta, owner) { - var embedded = meta.options.embedded, - collectionClass = embedded ? Ember.EmbeddedHasManyArray : Ember.HasManyArray; - - var collection = collectionClass.create({ - parent: this, - modelClass: type, - content: this._getHasManyContent(key, type, embedded), - embedded: embedded, - key: key, - relationshipKey: meta.relationshipKey - }); + }).meta(meta); + }; - Ember.setOwner(collection, owner); - this._registerHasManyArray(collection); + Ember.Model.reopen({ + getHasMany: function(key, type, meta, container) { + var embedded = meta.options.embedded, + collectionClass = embedded ? Ember.EmbeddedHasManyArray : Ember.HasManyArray; + + var collection = collectionClass.create({ + parent: this, + modelClass: type, + content: this._getHasManyContent(key, type, embedded), + embedded: embedded, + key: key, + relationshipKey: meta.relationshipKey, + container: container + }); - return collection; - } -}); + this._registerHasManyArray(collection); + return collection; + } + }); -})(); -(function() { + })(); -var get = Ember.get, - set = Ember.set; + (function() { -function storeFor(record) { - var owner = Ember.getOwner(record); + var get = Ember.get, + set = Ember.set; - if (owner) { - return owner.lookup('emstore:main'); - } + function storeFor(record) { + if (record.container) { + return record.container.lookup('store:main'); + } - return null; -} + return null; + } -function getType(record) { - var type = this.type; + function getType(record, type) { - if (typeof this.type === "string" && this.type) { - type = Ember.get(Ember.lookup, this.type); + if (typeof this.type === "string" && this.type) { + type = Ember.get(Ember.lookup, this.type); - if (!type) { - var emstore = storeFor(record); - type = emstore.modelFor(this.type); - type.reopenClass({ adapter: emstore.adapterFor(this.type) }); + if (!type) { + var store = storeFor(record); + type = store.modelFor(this.type); + type.reopenClass({ adapter: store.adapterFor(this.type) }); + } } + + return type; } - return type; -} + Ember.belongsTo = function(type, options) { + options = options || {}; -Ember.belongsTo = function(type, options) { - options = options || {}; + var meta = { type: type, isRelationship: true, options: options, kind: 'belongsTo', getType: getType}; - var meta = { type: type, isRelationship: true, options: options, kind: 'belongsTo', getType: getType}; + return Ember.computed("_data", { + get: function(propertyKey){ + var innerType = meta.getType(this, type); + Ember.assert("Type cannot be empty.", !Ember.isEmpty(innerType)); - return Ember.Model.computed("_data", { - get: function(propertyKey){ - type = meta.getType(this); - Ember.assert("Type cannot be empty.", !Ember.isEmpty(type)); + var key = options.key || propertyKey, + self = this; - var key = options.key || propertyKey, - self = this; + var dirtyChanged = function(sender) { + if (sender.get('isDirty')) { + self._relationshipBecameDirty(propertyKey); + } else { + self._relationshipBecameClean(propertyKey); + } + }; - var dirtyChanged = function(sender) { - if (sender.get('isDirty')) { - self._relationshipBecameDirty(propertyKey); - } else { - self._relationshipBecameClean(propertyKey); + var store = storeFor(this), + value = this.getBelongsTo(key, innerType, meta, store); + this._registerBelongsTo(meta); + if (value !== null && meta.options.embedded) { + value.get('isDirty'); // getter must be called before adding observer + value.addObserver('isDirty', dirtyChanged); } - }; - - var emstore = storeFor(this), - value = this.getBelongsTo(key, type, meta, emstore); - this._registerBelongsTo(meta); - if (value !== null && meta.options.embedded) { - value.get('isDirty'); // getter must be called before adding observer - value.addObserver('isDirty', dirtyChanged); - } - return value; - }, + return value; + }, + + set: function(propertyKey, value, oldValue){ + var innerType = meta.getType(this, type); + Ember.assert("Type cannot be empty.", !Ember.isEmpty(innerType)); + + var dirtyAttributes = get(this, '_dirtyAttributes'), + createdDirtyAttributes = false, + self = this; + + var dirtyChanged = function(sender) { + if (sender.get('isDirty')) { + self._relationshipBecameDirty(propertyKey); + } else { + self._relationshipBecameClean(propertyKey); + } + }; - set: function(propertyKey, value, oldValue){ - type = meta.getType(this); - Ember.assert("Type cannot be empty.", !Ember.isEmpty(type)); + if (!dirtyAttributes) { + dirtyAttributes = []; + createdDirtyAttributes = true; + } - var dirtyAttributes = get(this, '_dirtyAttributes'), - createdDirtyAttributes = false, - self = this; + if (value) { + Ember.assert(`Attempted to set property of type: ${value.constructor} with a value of type: ${innerType}`, + value instanceof innerType); + } - var dirtyChanged = function(sender) { - if (sender.get('isDirty')) { - self._relationshipBecameDirty(propertyKey); + if (oldValue !== value) { + dirtyAttributes.pushObject(propertyKey); } else { - self._relationshipBecameClean(propertyKey); + dirtyAttributes.removeObject(propertyKey); } - }; - if (!dirtyAttributes) { - dirtyAttributes = []; - createdDirtyAttributes = true; - } + if (createdDirtyAttributes) { + set(this, '_dirtyAttributes', dirtyAttributes); + } - if (value) { - Ember.assert('Attempted to set property of type: ' + value.constructor + ' with a value of type: ' + type, value instanceof type); - } + if (meta.options.embedded) { + if (oldValue) { + oldValue.removeObserver('isDirty', dirtyChanged); + } + if (value) { + value.addObserver('isDirty', dirtyChanged); + } + } - if (oldValue !== value) { - dirtyAttributes.pushObject(propertyKey); - } else { - dirtyAttributes.removeObject(propertyKey); + return value === undefined ? null : value; } + }).meta(meta); + }; + + Ember.Model.reopen({ + getBelongsTo: function(key, type, meta, store) { + var idOrAttrs = get(this, '_data.' + key), + record; - if (createdDirtyAttributes) { - set(this, '_dirtyAttributes', dirtyAttributes); + if (Ember.isNone(idOrAttrs)) { + return null; } if (meta.options.embedded) { - if (oldValue) { - oldValue.removeObserver('isDirty', dirtyChanged); - } - if (value) { - value.addObserver('isDirty', dirtyChanged); + var primaryKey = get(type, 'primaryKey'), + id = idOrAttrs[primaryKey]; + record = type.create({ isLoaded: false, id: id, container: this.container }); + record.load(id, idOrAttrs); + } else { + if (store) { + record = store._findSync(meta.type, idOrAttrs); + } else { + record = type.find(idOrAttrs); } } - return value === undefined ? null : value; - } - }).meta(meta); -}; - -Ember.Model.reopen({ - getBelongsTo: function(key, type, meta, emstore) { - var idOrAttrs = get(this, '_data.' + key), - record; - - if (Ember.isNone(idOrAttrs)) { - return null; + return record; } + }); - if (meta.options.embedded) { - var primaryKey = get(type, 'primaryKey'), - id = idOrAttrs[primaryKey]; - record = type.create({ isLoaded: false, id: id }); - var owner = Ember.getOwner(this); - Ember.setOwner(record, owner); + })(); - record.load(id, idOrAttrs); - } else { - if (emstore) { - record = emstore._findSync(meta.type, idOrAttrs); - } else { - record = type.find(idOrAttrs); - } - } + (function() { - return record; - } -}); + var get = Ember.get, + set = Ember.set, + meta = Ember.meta; + Ember.Model.dataTypes = {}; -})(); + Ember.Model.dataTypes[Date] = { + deserialize: function(string) { + if (!string) { return null; } + return new Date(string); + }, + serialize: function (date) { + if (!date) { return null; } + return date.toISOString(); + }, + isEqual: function(obj1, obj2) { + if (obj1 instanceof Date) { obj1 = this.serialize(obj1); } + if (obj2 instanceof Date) { obj2 = this.serialize(obj2); } + return obj1 === obj2; + } + }; -(function() { + Ember.Model.dataTypes[Number] = { + deserialize: function(string) { + if (!string && string !== 0) { return null; } + return Number(string); + }, + serialize: function (number) { + if (!number && number !== 0) { return null; } + return Number(number); + } + }; -var get = Ember.get, - set = Ember.set, - meta = Ember.meta; - -Ember.Model.dataTypes = {}; - -Ember.Model.dataTypes[Date] = { - deserialize: function(string) { - if (!string) { return null; } - return new Date(string); - }, - serialize: function (date) { - if (!date) { return null; } - return date.toISOString(); - }, - isEqual: function(obj1, obj2) { - if (obj1 instanceof Date) { obj1 = this.serialize(obj1); } - if (obj2 instanceof Date) { obj2 = this.serialize(obj2); } - return obj1 === obj2; - } -}; - -Ember.Model.dataTypes[Number] = { - deserialize: function(string) { - if (!string && string !== 0) { return null; } - return Number(string); - }, - serialize: function (number) { - if (!number && number !== 0) { return null; } - return Number(number); - } -}; - -function deserialize(value, type) { - if (type && type.deserialize) { - return type.deserialize(value); - } else if (type && Ember.Model.dataTypes[type]) { - return Ember.Model.dataTypes[type].deserialize(value); - } else { - return value; - } -} - -function serialize(value, type) { - if (type && type.serialize) { - return type.serialize(value); - } else if (type && Ember.Model.dataTypes[type]) { - return Ember.Model.dataTypes[type].serialize(value); - } else { - return value; + function deserialize(value, type) { + if (type && type.deserialize) { + return type.deserialize(value); + } else if (type && Ember.Model.dataTypes[type]) { + return Ember.Model.dataTypes[type].deserialize(value); + } else { + return value; + } } -} -Ember.attr = function(type, options) { - return Ember.Model.computed("_data", { - get: function(key){ - var data = get(this, '_data'), - dataKey = this.dataKey(key), - dataValue = data && get(data, dataKey); - - if (dataValue==null && options && options.defaultValue!=null) { - return Ember.copy(options.defaultValue); - } + function serialize(value, type) { + if (type && type.serialize) { + return type.serialize(value); + } else if (type && Ember.Model.dataTypes[type]) { + return Ember.Model.dataTypes[type].serialize(value); + } else { + return value; + } + } - return this.getAttr(key, deserialize(dataValue, type)); - }, - set: function(key, value){ - var data = get(this, '_data'), - dataKey = this.dataKey(key), - dataValue = data && get(data, dataKey), - beingCreated = meta(this).proto === this, - dirtyAttributes = get(this, '_dirtyAttributes'), - createdDirtyAttributes = false; - if (!dirtyAttributes) { - dirtyAttributes = Ember.A([]); - createdDirtyAttributes = true; - } + Ember.attr = function(type, options) { + return Ember.computed("_data", { + get: function(key){ + var data = get(this, '_data'), + dataKey = this.dataKey(key), + dataValue = data && get(data, dataKey); - if (beingCreated) { - if (!data) { - data = {}; - set(this, '_data', data); + if (dataValue==null && options && options.defaultValue!=null) { + return Ember.copy(options.defaultValue); } - dataValue = data[dataKey] = value; - } - - if (dataValue !== serialize(value, type)) { - dirtyAttributes.pushObject(key); - } else { - dirtyAttributes.removeObject(key); - } - if (createdDirtyAttributes) { - set(this, '_dirtyAttributes', dirtyAttributes); - } + return this.getAttr(key, deserialize(dataValue, type)); + }, + set: function(key, value){ + var data = get(this, '_data'), + dataKey = this.dataKey(key), + dataValue = data && get(data, dataKey), + beingCreated = meta(this).proto === this, + dirtyAttributes = get(this, '_dirtyAttributes'), + createdDirtyAttributes = false, + newValue, + isDirty; + if (!dirtyAttributes) { + dirtyAttributes = Ember.A([]); + createdDirtyAttributes = true; + } - return value; - } - }).meta({isAttribute: true, type: type, options: options}); -}; + if (beingCreated) { + if (!data) { + data = {}; + set(this, '_data', data); + } + dataValue = data[dataKey] = value; + } + // PATCH(mattwright): Checking `dataValue !== serialize(value, type)` will + // always evaluate as dirty for attributes that are objects (Money attrs + // for example), so we add special handling here. + newValue = serialize(value, type); + if (typeof newValue === "object") { + isDirty = JSON.stringify(dataValue) !== JSON.stringify(newValue); + } else { + isDirty = dataValue !== newValue; + } -})(); + if (isDirty) { + dirtyAttributes.pushObject(key); + } else { + dirtyAttributes.removeObject(key); + } -(function() { + if (createdDirtyAttributes) { + set(this, '_dirtyAttributes', dirtyAttributes); + } -var get = Ember.get; + return value; + } + }).meta({isAttribute: true, type: type, options: options}); + }; -Ember.RESTAdapter = Ember.Adapter.extend({ - find: function(record, id) { - var url = this.buildURL(record.constructor, id), - self = this; - return this.ajax(url).then(function(data) { - self.didFind(record, id, data); - return record; - }); - }, + })(); - didFind: function(record, id, data) { - var rootKey = get(record.constructor, 'rootKey'), - dataToLoad = rootKey ? get(data, rootKey) : data; + (function() { - record.load(id, dataToLoad); - }, + var get = Ember.get; - findAll: function(klass, records) { - var url = this.buildURL(klass), - self = this; + Ember.RESTAdapter = Ember.Adapter.extend({ + find: function(record, id) { + var url = this.buildURL(record.constructor, id), + self = this; - return this.ajax(url).then(function(data) { - self.didFindAll(klass, records, data); - return records; - }); - }, + return this.ajax(url).then(function(data) { + self.didFind(record, id, data); + return record; + }); + }, - didFindAll: function(klass, records, data) { - var collectionKey = get(klass, 'collectionKey'), - dataToLoad = collectionKey ? get(data, collectionKey) : data; + didFind: function(record, id, data) { + var rootKey = get(record.constructor, 'rootKey'), + dataToLoad = rootKey ? get(data, rootKey) : data; - records.load(klass, dataToLoad); - }, + record.load(id, dataToLoad); + }, - findQuery: function(klass, records, params) { - var url = this.buildURL(klass), - self = this; + findAll: function(klass, records) { + var url = this.buildURL(klass), + self = this; - return this.ajax(url, params).then(function(data) { - self.didFindQuery(klass, records, params, data); - return records; - }); - }, + return this.ajax(url).then(function(data) { + self.didFindAll(klass, records, data); + return records; + }); + }, - didFindQuery: function(klass, records, params, data) { + didFindAll: function(klass, records, data) { var collectionKey = get(klass, 'collectionKey'), dataToLoad = collectionKey ? get(data, collectionKey) : data; records.load(klass, dataToLoad); - }, + }, - createRecord: function(record) { - var url = this.buildURL(record.constructor), - self = this; + findQuery: function(klass, records, params) { + var url = this.buildURL(klass), + self = this; - return this.ajax(url, record.toJSON(), "POST").then(function(data) { - self.didCreateRecord(record, data); - return record; - }); - }, + return this.ajax(url, params).then(function(data) { + self.didFindQuery(klass, records, params, data); + return records; + }); + }, - didCreateRecord: function(record, data) { - this._loadRecordFromData(record, data); - record.didCreateRecord(); - }, + didFindQuery: function(klass, records, params, data) { + var collectionKey = get(klass, 'collectionKey'), + dataToLoad = collectionKey ? get(data, collectionKey) : data; - saveRecord: function(record) { - var primaryKey = get(record.constructor, 'primaryKey'), - url = this.buildURL(record.constructor, get(record, primaryKey)), - self = this; + records.load(klass, dataToLoad); + }, - return this.ajax(url, record.toJSON(), "PUT").then(function(data) { // TODO: Some APIs may or may not return data - self.didSaveRecord(record, data); - return record; - }); - }, + createRecord: function(record) { + var url = this.buildURL(record.constructor), + self = this; - didSaveRecord: function(record, data) { - this._loadRecordFromData(record, data); - record.didSaveRecord(); - }, + return this.ajax(url, record.toJSON(), "POST").then(function(data) { + self.didCreateRecord(record, data); + return record; + }); + }, - deleteRecord: function(record) { - var primaryKey = get(record.constructor, 'primaryKey'), - url = this.buildURL(record.constructor, get(record, primaryKey)), - self = this; + didCreateRecord: function(record, data) { + this._loadRecordFromData(record, data); + record.didCreateRecord(); + }, - return this.ajax(url, record.toJSON(), "DELETE").then(function(data) { - self.didDeleteRecord(record, data); - }); - }, + saveRecord: function(record) { + var primaryKey = get(record.constructor, 'primaryKey'), + url = this.buildURL(record.constructor, get(record, primaryKey)), + self = this; - didDeleteRecord: function(record, data) { - record.didDeleteRecord(); - }, + return this.ajax(url, record.toJSON(), "PUT").then(function(data) { // TODO: Some APIs may or may not return data + self.didSaveRecord(record, data); + return record; + }); + }, - ajax: function(url, params, method, settings) { - return this._ajax(url, params, (method || "GET"), settings); - }, + didSaveRecord: function(record, data) { + this._loadRecordFromData(record, data); + record.didSaveRecord(); + }, - buildURL: function(klass, id) { - var urlRoot = get(klass, 'url'); - var urlSuffix = get(klass, 'urlSuffix') || ''; - if (!urlRoot) { throw new Error('Ember.RESTAdapter requires a `url` property to be specified'); } + deleteRecord: function(record) { + var primaryKey = get(record.constructor, 'primaryKey'), + url = this.buildURL(record.constructor, get(record, primaryKey)), + self = this; - if (!Ember.isEmpty(id)) { - return urlRoot + "/" + id + urlSuffix; - } else { - return urlRoot + urlSuffix; - } - }, + return this.ajax(url, record.toJSON(), "DELETE").then(function(data) { + self.didDeleteRecord(record, data); + }); + }, - ajaxSettings: function(url, method) { - return { - url: url, - type: method, - dataType: "json" - }; - }, + didDeleteRecord: function(record, data) { + record.didDeleteRecord(); + }, - _ajax: function(url, params, method, settings) { - var self = this; - if (!settings) { - settings = this.ajaxSettings(url, method); - } + ajax: function(url, params, method, settings) { + return this._ajax(url, params, (method || "GET"), settings); + }, - return new Ember.RSVP.Promise(function(resolve, reject) { - if (params) { - if (method === "GET") { - settings.data = params; - } else { - settings.contentType = "application/json; charset=utf-8"; - settings.data = JSON.stringify(params); - } + buildURL: function(klass, id) { + var urlRoot = get(klass, 'url'); + var urlSuffix = get(klass, 'urlSuffix') || ''; + if (!urlRoot) { throw new Error('Ember.RESTAdapter requires a `url` property to be specified'); } + + if (!Ember.isEmpty(id)) { + return urlRoot + "/" + id + urlSuffix; + } else { + return urlRoot + urlSuffix; } + }, - settings.success = function(json) { - Ember.run(null, resolve, json); + ajaxSettings: function(url, method) { + return { + url: url, + type: method, + dataType: "json" }; + }, + + _ajax: function(url, params, method, settings) { + var self = this; + if (!settings) { + settings = this.ajaxSettings(url, method); + } - settings.error = function(jqXHR, textStatus, errorThrown) { - // https://github.com/ebryn/ember-model/issues/202 - if (jqXHR && typeof jqXHR === 'object') { - jqXHR.then = null; + return new Ember.RSVP.Promise(function(resolve, reject) { + if (params) { + if (method === "GET") { + settings.data = params; + } else { + settings.contentType = "application/json; charset=utf-8"; + settings.data = JSON.stringify(params); + } } - self._handleRejections(method, jqXHR, resolve, reject); - }; + settings.success = function(json) { + Ember.run(null, resolve, json); + }; + settings.error = function(jqXHR, textStatus, errorThrown) { + // https://github.com/ebryn/ember-model/issues/202 + if (jqXHR && typeof jqXHR === 'object') { + jqXHR.then = null; + } - Ember.$.ajax(settings); - }, "ember-model: RESTAdapter#ajax ${method} to " + url); - }, + self._handleRejections(method, jqXHR, resolve, reject); + }; - _handleRejections: function(method, jqXHR, resolve, reject) { - if (method === "DELETE" && jqXHR.status >= 200 && jqXHR.status < 300) { - Ember.run(null, resolve, null); - } else { - Ember.run(null, reject, jqXHR); - } - }, - _loadRecordFromData: function(record, data) { - var rootKey = get(record.constructor, 'rootKey'), - primaryKey = get(record.constructor, 'primaryKey'); - // handle HEAD response where no data is provided by server - if (data) { - data = rootKey ? get(data, rootKey) : data; - if (!Ember.isEmpty(data)) { - record.load(data[primaryKey], data); + Ember.$.ajax(settings); + }); + }, + + _handleRejections: function(method, jqXHR, resolve, reject) { + if (method === "DELETE" && jqXHR.status >= 200 && jqXHR.status < 300) { + Ember.run(null, resolve, null); + } else { + Ember.run(null, reject, jqXHR); } - } - } -}); + }, + _loadRecordFromData: function(record, data) { + var rootKey = get(record.constructor, 'rootKey'), + primaryKey = get(record.constructor, 'primaryKey'); + // handle HEAD response where no data is provided by server + if (data) { + data = rootKey ? get(data, rootKey) : data; + if (!Ember.isEmpty(data)) { + record.load(data[primaryKey], data); + } + } + } + }); -})(); -(function() { + })(); -var get = Ember.get; + (function() { -Ember.loadPromise = function(target) { - if (Ember.isNone(target)) { - return null; - } else if (target.then) { - return target; - } else { - var deferred = Ember.RSVP.defer(); + var get = Ember.get; - if (get(target, 'isLoaded') && !get(target, 'isNew')) { - deferred.resolve(target); + Ember.loadPromise = function(target) { + if (Ember.isNone(target)) { + return null; + } else if (target.then) { + return target; } else { - target.one('didLoad', this, function() { + var deferred = Ember.RSVP.defer(); + + if (get(target, 'isLoaded') && !get(target, 'isNew')) { deferred.resolve(target); - }); + } else { + target.one('didLoad', this, function() { + deferred.resolve(target); + }); + } + + return deferred.promise; } + }; - return deferred.promise; - } -}; + })(); -})(); + (function() { -(function() { + // This is a debug adapter for the Ember Extension, don't let the fact this is called an "adapter" confuse you. + // Most copied from: https://github.com/emberjs/data/blob/master/packages/ember-data/lib/system/debug/debug_adapter.js -// This is a debug adapter for the Ember Extension, don't let the fact this is called an "adapter" confuse you. -// Most copied from: https://github.com/emberjs/data/blob/master/packages/ember-data/lib/system/debug/debug_adapter.js + if (!Ember.DataAdapter) { return; } -if (!Ember.DataAdapter) { return; } + var get = Ember.get, capitalize = Ember.String.capitalize, underscore = Ember.String.underscore; -var get = Ember.get, capitalize = Ember.String.capitalize, underscore = Ember.String.underscore; + var DebugAdapter = Ember.DataAdapter.extend({ + getFilters: function() { + return [ + { name: 'isNew', desc: 'New' }, + { name: 'isModified', desc: 'Modified' }, + { name: 'isClean', desc: 'Clean' } + ]; + }, -var DebugAdapter = Ember.DataAdapter.extend({ - getFilters: function() { - return [ - { name: 'isNew', desc: 'New' }, - { name: 'isModified', desc: 'Modified' }, - { name: 'isClean', desc: 'Clean' } - ]; - }, + detect: function(klass) { + return klass !== Ember.Model && Ember.Model.detect(klass); + }, - detect: function(klass) { - return klass !== Ember.Model && Ember.Model.detect(klass); - }, + columnsForType: function(type) { + var columns = [], count = 0, self = this; + type.getAttributes().forEach(function(name, meta) { + if (count++ > self.attributeLimit) { return false; } + var desc = capitalize(underscore(name).replace('_', ' ')); + columns.push({ name: name, desc: desc }); + }); + return columns; + }, - columnsForType: function(type) { - var columns = [], count = 0, self = this; - type.getAttributes().forEach(function(name, meta) { - if (count++ > self.attributeLimit) { return false; } - var desc = capitalize(underscore(name).replace('_', ' ')); - columns.push({ name: name, desc: desc }); - }); - return columns; - }, + getRecords: function(type) { + var records = []; + type.forEachCachedRecord(function(record) { records.push(record); }); + return records; + }, - getRecords: function(type) { - var records = []; - type.forEachCachedRecord(function(record) { records.push(record); }); - return records; - }, + getRecordColumnValues: function(record) { + var self = this, count = 0, + columnValues = { id: get(record, 'id') }; - getRecordColumnValues: function(record) { - var self = this, count = 0, - columnValues = { id: get(record, 'id') }; + record.constructor.getAttributes().forEach(function(key) { + if (count++ > self.attributeLimit) { + return false; + } + var value = get(record, key); + columnValues[key] = value; + }); + return columnValues; + }, - record.constructor.getAttributes().forEach(function(key) { - if (count++ > self.attributeLimit) { - return false; - } - var value = get(record, key); - columnValues[key] = value; - }); - return columnValues; - }, + getRecordKeywords: function(record) { + var keywords = [], keys = Ember.A(['id']); + record.constructor.getAttributes().forEach(function(key) { + keys.push(key); + }); + keys.forEach(function(key) { + keywords.push(get(record, key)); + }); + return keywords; + }, - getRecordKeywords: function(record) { - var keywords = [], keys = Ember.A(['id']); - record.constructor.getAttributes().forEach(function(key) { - keys.push(key); - }); - keys.forEach(function(key) { - keywords.push(get(record, key)); - }); - return keywords; - }, - - getRecordFilterValues: function(record) { - return { - isNew: record.get('isNew'), - isModified: record.get('isDirty') && !record.get('isNew'), - isClean: !record.get('isDirty') - }; - }, - - getRecordColor: function(record) { - var color = 'black'; - if (record.get('isNew')) { - color = 'green'; - } else if (record.get('isDirty')) { - color = 'blue'; - } - return color; - }, + getRecordFilterValues: function(record) { + return { + isNew: record.get('isNew'), + isModified: record.get('isDirty') && !record.get('isNew'), + isClean: !record.get('isDirty') + }; + }, - observeRecord: function(record, recordUpdated) { - var releaseMethods = Ember.A(), self = this, - keysToObserve = Ember.A(['id', 'isNew', 'isDirty']); + getRecordColor: function(record) { + var color = 'black'; + if (record.get('isNew')) { + color = 'green'; + } else if (record.get('isDirty')) { + color = 'blue'; + } + return color; + }, - record.constructor.getAttributes().forEach(function(key) { - keysToObserve.push(key); - }); + observeRecord: function(record, recordUpdated) { + var releaseMethods = Ember.A(), self = this, + keysToObserve = Ember.A(['id', 'isNew', 'isDirty']); - keysToObserve.forEach(function(key) { - var handler = function() { - recordUpdated(self.wrapRecord(record)); - }; - Ember.addObserver(record, key, handler); - releaseMethods.push(function() { - Ember.removeObserver(record, key, handler); + record.constructor.getAttributes().forEach(function(key) { + keysToObserve.push(key); }); - }); - - var release = function() { - releaseMethods.forEach(function(fn) { fn(); } ); - }; - return release; - } -}); + keysToObserve.forEach(function(key) { + var handler = function() { + recordUpdated(self.wrapRecord(record)); + }; + Ember.addObserver(record, key, handler); + releaseMethods.push(function() { + Ember.removeObserver(record, key, handler); + }); + }); -Ember.onLoad('Ember.Application', function(Application) { - Application.initializer({ - name: "em-data-adapter", + var release = function() { + releaseMethods.forEach(function(fn) { fn(); } ); + }; - initialize: function() { - var application = arguments[1] || arguments[0]; - application.register('em-data-adapter:main', DebugAdapter); + return release; } }); -}); + Ember.onLoad('Ember.Application', function(Application) { + Application.initializer({ + name: "data-adapter", -})(); + initialize: function() { + var application = arguments[1] || arguments[0]; + application.register('data-adapter:main', DebugAdapter); + } + }); + }); -(function() { -function NIL() {} + })(); -Ember.Model.Store = Ember.Service.extend({ - modelFor: function(type) { - return Ember.getOwner(this).factoryFor('model:' + type).class; - }, + (function() { - adapterFor: function(type) { - var adapter = this.modelFor(type).adapter; - var owner = Ember.getOwner(this); + function NIL() {} - if (adapter && adapter !== Ember.Model.adapter) { - return adapter; - } else { - adapter = owner.factoryFor('adapter:' + type) || - owner.factoryFor('adapter:application') || - owner.factoryFor('adapter:REST'); + Ember.Model.Store = Ember.Object.extend({ + // Prod builds (but not dev builds) of Ember 2.13 seem to blow up if you + // define an attribute called `container`. I (mattwright) created an issue + // with more details here: https://github.com/ebryn/ember-model/issues/457 + // container: null, - return adapter ? adapter.create() : adapter; - } - }, + modelFor: function(type) { + return this.container.lookupFactory('model:'+type); + }, - createRecord: function(type, props) { - var klass = this.modelFor(type); - klass.reopenClass({adapter: this.adapterFor(type)}); + adapterFor: function(type) { + var adapter = this.modelFor(type).adapter, + container = this.container; - var record = klass.create(props); + if (adapter && adapter !== Ember.Model.adapter) { + return adapter; + } else { + adapter = container.lookupFactory('adapter:'+ type) || + container.lookupFactory('adapter:application') || + container.lookupFactory('adapter:REST'); - var owner = Ember.getOwner(this); - Ember.setOwner(record, owner); + return adapter ? adapter.create() : adapter; + } + }, - return record; - }, + createRecord: function(type, props) { + var klass = this.modelFor(type); + klass.reopenClass({adapter: this.adapterFor(type)}); + return klass.create(Ember.merge({container: this.container}, props)); + }, - find: function(type, id) { - if (arguments.length === 1) { id = NIL; } - return this._find(type, id, true); - }, + find: function(type, id) { + if (arguments.length === 1) { id = NIL; } + return this._find(type, id, true); + }, - _find: function(type, id, isAsync) { - var klass = this.modelFor(type); + _find: function(type, id, async) { + var klass = this.modelFor(type); - // if (!klass.adapter) { - klass.reopenClass({adapter: this.adapterFor(type)}); - // } + // if (!klass.adapter) { + klass.reopenClass({adapter: this.adapterFor(type)}); + // } - var owner = Ember.getOwner(this); + if (id === NIL) { + return klass._findFetchAll(async, this.container); + } else if (Ember.isArray(id)) { + return klass._findFetchMany(id, async, this.container); + } else if (typeof id === 'object') { + return klass._findFetchQuery(id, async, this.container); + } else { + return klass._findFetchById(id, async, this.container); + } + }, - if (id === NIL) { - return klass._findFetchAll(isAsync, owner); - } else if (Ember.isArray(id)) { - return klass._findFetchMany(id, isAsync, owner); - } else if (typeof id === 'object') { - return klass._findFetchQuery(id, isAsync, owner); - } else { - return klass._findFetchById(id, isAsync, owner); + _findSync: function(type, id) { + return this._find(type, id, false); } - }, + }); - _findSync: function(type, id) { - return this._find(type, id, false); - } -}); + Ember.onLoad('Ember.Application', function(Application) { -Ember.onLoad('Ember.Application', function(Application) { + Application.initializer({ + name: "store", - Application.initializer({ - name: "emstore", + initialize: function() { + var application = arguments[1] || arguments[0]; + var store = application.Store || Ember.Model.Store; + application.register('store:application', store); + application.register('store:main', store); - initialize: function() { - var application = arguments[1] || arguments[0]; - var store = application.Store || Ember.Model.Store; - application.register('emstore:application', store); - application.register('emstore:main', store); + application.inject('route', 'store', 'store:main'); + application.inject('controller', 'store', 'store:main'); + } + }); - application.inject('route', 'emstore', 'emstore:main'); - application.inject('controller', 'emstore', 'emstore:main'); - } }); -}); - -})(); + })(); diff --git a/package.json b/package.json index f1c351e..1758b85 100644 --- a/package.json +++ b/package.json @@ -1,38 +1,68 @@ { "name": "ember-model", "description": "A lightweight model library for Ember.js", - "main": "Gruntfile.js", + + "repository": "", + "license": "MIT", + "author": "", "directories": { "test": "tests" }, "scripts": { - "test": "grunt test" + "build": "ember build", + "lint:js": "eslint ./*.js addon addon-test-support app config lib server test-support tests", + "start": "ember serve", + "test": "ember try:each" }, - "repository": { - "type": "git", - "url": "git://github.com/ebryn/ember-model.git" + "dependencies": { + "broccoli-funnel": "1.1.0", + "broccoli-merge-trees": "^2.0.0", + "ember-cli-babel": "^6.6.0" }, "author": "no", "license": "MIT", "devDependencies": { - "ember-source": "3.0.0", - "glob": "~3.2.1", + "broccoli-asset-rev": "^2.4.5", + "ember-ajax": "^3.0.0", + "ember-cli": "~3.0.4", + "ember-cli-dependency-checker": "^2.0.0", + "ember-cli-eslint": "^4.2.1", + "ember-cli-htmlbars": "^2.0.1", + "ember-cli-htmlbars-inline-precompile": "^1.0.0", + "ember-cli-inject-live-reload": "^1.4.1", + "ember-cli-qunit": "^4.1.1", + "ember-cli-shims": "^1.2.0", + "ember-cli-sri": "^2.1.0", + "ember-cli-uglify": "^2.0.0", + "ember-disable-prototype-extensions": "^1.1.2", + "ember-export-application-global": "^2.0.0", + "ember-load-initializers": "^1.0.0", + "ember-maybe-import-regenerator": "^0.1.6", + "ember-resolver": "^4.0.0", + "ember-source": "~3.2.2", + "ember-source-channel-url": "^1.0.1", + "ember-try": "^0.2.23", + "ember-welcome-page": "^3.0.0", + "eslint-plugin-ember": "^5.0.0", + "eslint-plugin-node": "^6.0.1", "grunt": "~1.0.1", - "grunt-contrib-clean": "~0.4.1", - "grunt-contrib-connect": "~0.3.0", - "grunt-contrib-copy": "~0.4.1", - "grunt-contrib-jshint": "~0.6.0", - "grunt-contrib-uglify": "~0.2.2", + "grunt-neuter": "~0.6.0", + "grunt-contrib-jshint": "~1.1.0", + "grunt-contrib-uglify": "~2.0.0", + "grunt-contrib-qunit": "3.0.1", + "grunt-contrib-clean": "~1.0.0", + "grunt-strip-node": "~0.1.1", + "matchdep": "~1.0.1", + "grunt-curl": "~2.2.0", + "grunt-strip": "~0.2.1", + "grunt-release": "~0.14.0", + "glob": "4.4.2", + "grunt-contrib-copy": "~1.0.0", + "grunt-contrib-connect": "~1.0.2", "grunt-contrib-watch": "~1.0.0", "grunt-curl": "~1.1.1", "grunt-ember-s3": "~1.0.2", - "grunt-neuter": "~0.6.0", - "grunt-release": "git+https://github.com/trek/grunt-release.git#7b766605c85605", - "grunt-strip": "~0.2.1", - "grunt-strip-node": "~0.1.1", - "grunt-testem-mincer": "patocallaghan/grunt-testem-mincer#patoc/update-testem", - "matchdep": "~0.1.2", - "qunit": "~2.5" + "loader.js": "^4.2.3" }, - "version": "3.0.0" + "version": "0.0.18" } diff --git a/packages/ember-model/lib/adapter.js b/packages/ember-model/lib/adapter.js index d722d13..0cd9cbb 100644 --- a/packages/ember-model/lib/adapter.js +++ b/packages/ember-model/lib/adapter.js @@ -21,3 +21,9 @@ Ember.Adapter = Ember.Object.extend({ record.load(id, data); } }); + +Ember.Adapter.reopenClass({ + toString() { + return `Ember.Adapter`; + } +}); diff --git a/packages/ember-model/lib/attr.js b/packages/ember-model/lib/attr.js index a56ffa0..44efd44 100644 --- a/packages/ember-model/lib/attr.js +++ b/packages/ember-model/lib/attr.js @@ -53,7 +53,7 @@ function serialize(value, type) { } Ember.attr = function(type, options) { - return Ember.Model.computed("_data", { + return Ember.computed("_data", { get: function(key){ var data = get(this, '_data'), dataKey = this.dataKey(key), diff --git a/packages/ember-model/lib/belongs_to.js b/packages/ember-model/lib/belongs_to.js index ba0588b..9e52c37 100644 --- a/packages/ember-model/lib/belongs_to.js +++ b/packages/ember-model/lib/belongs_to.js @@ -12,8 +12,7 @@ function storeFor(record) { return null; } -function getType(record) { - var type = this.type; +function getType(record, type) { if (typeof this.type === "string" && this.type) { type = Ember.get(Ember.lookup, this.type); @@ -33,10 +32,10 @@ Ember.belongsTo = function(type, options) { var meta = { type: type, isRelationship: true, options: options, kind: 'belongsTo', getType: getType}; - return Ember.Model.computed("_data", { + return Ember.computed("_data", { get: function(propertyKey){ - type = meta.getType(this); - Ember.assert("Type cannot be empty.", !Ember.isEmpty(type)); + var innerType = meta.getType(this, type); + Ember.assert("Type cannot be empty.", !Ember.isEmpty(innerType)); var key = options.key || propertyKey, self = this; @@ -50,7 +49,7 @@ Ember.belongsTo = function(type, options) { }; var emstore = storeFor(this), - value = this.getBelongsTo(key, type, meta, emstore); + value = this.getBelongsTo(key, innerType, meta, emstore); this._registerBelongsTo(meta); if (value !== null && meta.options.embedded) { value.get('isDirty'); // getter must be called before adding observer @@ -60,8 +59,8 @@ Ember.belongsTo = function(type, options) { }, set: function(propertyKey, value, oldValue){ - type = meta.getType(this); - Ember.assert("Type cannot be empty.", !Ember.isEmpty(type)); + var innerType = meta.getType(this, type); + Ember.assert("Type cannot be empty.", !Ember.isEmpty(innerType)); var dirtyAttributes = get(this, '_dirtyAttributes'), createdDirtyAttributes = false, @@ -81,7 +80,8 @@ Ember.belongsTo = function(type, options) { } if (value) { - Ember.assert('Attempted to set property of type: ' + value.constructor + ' with a value of type: ' + type, value instanceof type); + Ember.assert(`Attempted to set property of type: ${value.constructor} with a value of type: ${innerType}`, + value instanceof innerType); } if (oldValue !== value) { diff --git a/packages/ember-model/lib/model.js b/packages/ember-model/lib/model.js index 1f4bdc4..3524279 100644 --- a/packages/ember-model/lib/model.js +++ b/packages/ember-model/lib/model.js @@ -59,10 +59,14 @@ Ember.Model = Ember.Object.extend(Ember.Evented, { return value; }, - isDirty: Ember.computed('_dirtyAttributes.length', function() { + toStringExtension() { + return this.id; + }, + + isDirty: function() { var dirtyAttributes = get(this, '_dirtyAttributes'); return dirtyAttributes && dirtyAttributes.length !== 0 || false; - }), + }.property('_dirtyAttributes'), _relationshipBecameDirty: function(name) { var dirtyAttributes = get(this, '_dirtyAttributes'); @@ -122,24 +126,22 @@ Ember.Model = Ember.Object.extend(Ember.Evented, { load: function(id, hash) { var data = {}; data[get(this.constructor, 'primaryKey')] = id; - set(this, '_data', Ember.merge(data, hash)); - this.getWithDefault('_dirtyAttributes', Ember.A([])).clear(); + data = Object.assign({}, data, hash); + set(this, '_data', data); + this.getWithDefault('_dirtyAttributes', []).clear(); this._reloadHasManys(); // eagerly load embedded data - var relationships = this.constructor._relationships || [], meta = Ember.meta(this), relationshipKey, relationship, relationshipMeta, relationshipData, relationshipType; - for (var i = 0, l = relationships.length; i < l; i++) { - relationshipKey = relationships[i]; - relationshipMeta = this.constructor.metaForProperty(relationshipKey); + for (let [relationshipKey, relationshipMeta] of this.constructor.relationships) { - if (relationshipMeta.options.embedded) { - relationshipType = relationshipMeta.type; + if (relationshipMeta.options.embedded && ! relationshipMeta.options.polymorphic) { + var relationshipType = relationshipMeta.type; relationshipType = relationshipMeta.type; if (typeof relationshipType === "string") { relationshipType = Ember.get(Ember.lookup, relationshipType) || Ember.getOwner(this).resolveRegistration('model:'+ relationshipType); } - relationshipData = data[relationshipKey]; + var relationshipData = data[relationshipKey]; if (relationshipData) { relationshipType.load(relationshipData); } @@ -177,47 +179,39 @@ Ember.Model = Ember.Object.extend(Ember.Evented, { var record = this.get(key); return record ? record.toJSON() : null; } else { - var primaryKey = get(meta.getType(this), 'primaryKey'); + var primaryKey = get(meta.getType(this, meta.type), 'primaryKey'); return this.get(key + '.' + primaryKey); } }, toJSON: function() { - var key, meta, - json = {}, - attributes = this.constructor.getAttributes(), - relationships = this.constructor.getRelationships(), - properties = attributes ? this.getProperties(attributes) : {}, - rootKey = get(this.constructor, 'rootKey'); - - for (key in properties) { - meta = this.constructor.metaForProperty(key); + var json = {}; + var rootKey = get(this.constructor, 'rootKey'); + + // Using ES5 getters feature here `this[key]` instead of + // this.get(key) + for (let [key, meta] of this.constructor.attributes) { if (meta.type && meta.type.serialize) { - json[this.dataKey(key)] = meta.type.serialize(properties[key]); + json[this.dataKey(key)] = meta.type.serialize(this[key]); } else if (meta.type && Ember.Model.dataTypes[meta.type]) { - json[this.dataKey(key)] = Ember.Model.dataTypes[meta.type].serialize(properties[key]); + json[this.dataKey(key)] = Ember.Model.dataTypes[meta.type].serialize(this[key]); } else { - json[this.dataKey(key)] = properties[key]; + json[this.dataKey(key)] = this[key]; } } - if (relationships) { - var data, relationshipKey; - - for(var i = 0; i < relationships.length; i++) { - key = relationships[i]; - meta = this.constructor.metaForProperty(key); - relationshipKey = meta.options.key || key; + for (let [key, meta] of this.constructor.relationships) { + let data; + let relationshipKey = meta.options.key || key; - if (meta.kind === 'belongsTo') { - data = this.serializeBelongsTo(key, meta); - } else { - data = this.serializeHasMany(key, meta); - } + if (meta.kind === 'belongsTo') { + data = this.serializeBelongsTo(key, meta); + } else { + data = this.serializeHasMany(key, meta); + } - json[relationshipKey] = data; + json[relationshipKey] = data; - } } if (rootKey) { @@ -402,24 +396,6 @@ Ember.Model.reopenClass({ _clientIdCounter: 1, - getAttributes: function() { - this.proto(); // force class "compilation" if it hasn't been done. - var attributes = this._attributes || []; - if (typeof this.superclass.getAttributes === 'function') { - attributes = this.superclass.getAttributes().concat(attributes); - } - return attributes; - }, - - getRelationships: function() { - this.proto(); // force class "compilation" if it hasn't been done. - var relationships = this._relationships || []; - if (typeof this.superclass.getRelationships === 'function') { - relationships = this.superclass.getRelationships().concat(relationships); - } - return relationships; - }, - fetch: function(id) { if (!arguments.length) { return this._findFetchAll(true); @@ -698,6 +674,29 @@ Ember.Model.reopenClass({ return record; }, + attributes: Ember.computed(function() { + let map = new Map(); + + this.eachComputedProperty((name, meta) => { + if (meta.isAttribute) { + meta.name = name; + map.set(name, meta); + } + }); + return map; + }).readOnly(), + + relationships: Ember.computed(function() { + let map = new Map(); + + this.eachComputedProperty((name, meta) => { + if (meta.isRelationship) { + meta.name = name; + map.set(name, meta); + } + }); + return map; + }).readOnly(), addToRecordArrays: function(record) { if (this._findAllRecordArray) { @@ -795,9 +794,9 @@ Ember.Model.reopenClass({ if (!this.sideloadedData) { this.sideloadedData = {}; } for (var i = 0, l = hashes.length; i < l; i++) { - var hash = hashes[i], - primaryKey = hash[get(this, 'primaryKey')], - record = this.getCachedReferenceRecord(primaryKey); + var hash = hashes[i]; + var primaryKey = hash[get(this, 'primaryKey')]; + var record = this.getCachedReferenceRecord(primaryKey); if (record) { record.load(primaryKey, hash); diff --git a/packages/ember-model/tests/adapter/custom_adapter_test.js b/packages/ember-model/tests/adapter/custom_adapter_test.js index 3e3641b..987c603 100644 --- a/packages/ember-model/tests/adapter/custom_adapter_test.js +++ b/packages/ember-model/tests/adapter/custom_adapter_test.js @@ -3,8 +3,13 @@ var CustomModel; QUnit.module("Ember.CustomAdapter", { beforeEach: function() { Ember.CustomAdapter = Ember.Adapter.extend(); + Ember.CustomAdapter.reopenClass({ + toString() { + return 'Ember.CustomAdapter'; + } + }); CustomModel = Ember.Model.extend({ - name: Ember.attr() + name: Ember.attr(), }); CustomModel.adapter = Ember.CustomAdapter.create(); } @@ -16,4 +21,4 @@ QUnit.test("throws an error message with class name", function(assert) { assert.throws(function() { Ember.run(CustomModel, CustomModel.find(1)); }, /Ember.CustomAdapter must implement find/); -}); \ No newline at end of file +}); diff --git a/packages/ember-model/tests/adapter/fixture_adapter_test.js b/packages/ember-model/tests/adapter/fixture_adapter_test.js index a6fb0da..124ebfb 100644 --- a/packages/ember-model/tests/adapter/fixture_adapter_test.js +++ b/packages/ember-model/tests/adapter/fixture_adapter_test.js @@ -17,10 +17,8 @@ QUnit.test("fetch loads the full FIXTURES payload when id isn't specified", func {id: 1, name: 'Erik'}, {id: 2, name: 'Aaron'} ]; - - FixtureModel.FIXTURES = data; - var done = assert.async(); + FixtureModel.FIXTURES = data; FixtureModel.fetch().then(function(records) { assert.equal(records.get('length'), data.length, "The proper number of items should have been loaded."); done(); @@ -67,6 +65,7 @@ QUnit.test("createRecord", function(assert) { FixtureModel.FIXTURES = []; var record = FixtureModel.create({name: "Erik"}); + Ember.setOwner(record, Ember.getOwner(FixtureModel)); assert.ok(record.get('isNew'), "Record should be new"); assert.ok(!record.get('id'), "Record #id should be undefined"); @@ -86,6 +85,8 @@ QUnit.test("createRecord - handle the case when the `rootKey` property is set", FixtureModel.FIXTURES = []; var record = FixtureModel.create({name: "Erik"}); + Ember.setOwner(record, Ember.getOwner(FixtureModel)); + var done = assert.async(); Ember.run(record, record.save).then(function () { diff --git a/packages/ember-model/tests/adapter/rest_adapter_test.js b/packages/ember-model/tests/adapter/rest_adapter_test.js index 3319abd..4e901a1 100644 --- a/packages/ember-model/tests/adapter/rest_adapter_test.js +++ b/packages/ember-model/tests/adapter/rest_adapter_test.js @@ -430,6 +430,7 @@ QUnit.test("createRecord", function(assert) { assert.expect(5); var record = RESTModel.create({name: "Erik"}); + Ember.setOwner(record, Ember.getOwner(RESTModel)); // ok(record.get('isDirty'), "Record should be dirty"); assert.ok(record.get('isNew'), "Record should be new"); @@ -451,6 +452,7 @@ QUnit.test("createRecord calls didCreateRecord", function(assert) { var record = RESTModel.create({name: "Erik"}), args, context, didCreateRecord = adapter.didCreateRecord, data = {post: {id: 1, name: "Erik"}}; + Ember.setOwner(record, Ember.getOwner(RESTModel)); // ok(record.get('isDirty'), "Record should be dirty"); assert.ok(record.get('isNew'), "Record should be new"); @@ -483,6 +485,8 @@ QUnit.test("createRecord record loads data in response", function(assert) { return ajaxSuccess(data); }; + Ember.setOwner(record, Ember.getOwner(RESTModel)); + Ember.run(record, record.save); assert.equal(record.get('id'), 1, 'resolved record should have id'); @@ -493,6 +497,7 @@ QUnit.test("saveRecord", function(assert) { assert.expect(5); var record = Ember.run(RESTModel, RESTModel.create, {id: 1, name: "Erik", isNew: false}); + Ember.setOwner(record, Ember.getOwner(RESTModel)); record.set('name', "Kris"); assert.ok(record.get('isDirty'), "Record should be dirty"); @@ -515,6 +520,8 @@ QUnit.test("saveRecord calls didSaveRecord after saving record", function(assert var record = Ember.run(RESTModel, RESTModel.create, {id: 1, name: "Erik", isNew: false}), data = {id: 1, name: "Erik"}, args, didSaveRecord = adapter.didSaveRecord, context; + Ember.setOwner(record, Ember.getOwner(RESTModel)); + record.set('name', "Kris"); assert.ok(record.get('isDirty'), "Record should be dirty"); @@ -542,6 +549,8 @@ QUnit.test("saveRecord loads response data if it exists", function(assert) { var record = Ember.run(RESTModel, RESTModel.create, {id: 1, name: "Erik", isNew: false}), responseData = {post: {id: 1, name: "Bill"}}; + Ember.setOwner(record, Ember.getOwner(RESTModel)); + record.set('name', 'John'); assert.ok(record.get('isDirty'), 'Record should be dirty'); @@ -561,6 +570,8 @@ QUnit.test("saveRecord does not load empty response", function(assert) { var record = Ember.run(RESTModel, RESTModel.create, {id: 1, name: "Erik", isNew: false}), responseData = ''; + Ember.setOwner(record, Ember.getOwner(RESTModel)); + record.set('name', 'John'); assert.ok(record.get('isDirty'), 'Record should be dirty'); @@ -580,6 +591,8 @@ QUnit.test("saveRecord does not load HEAD response (undefined response body)", f var record = Ember.run(RESTModel, RESTModel.create, {id: 1, name: "Erik", isNew: false}), responseData = ''; + Ember.setOwner(record, Ember.getOwner(RESTModel)); + record.set('name', 'John'); assert.ok(record.get('isDirty'), 'Record should be dirty'); @@ -599,6 +612,8 @@ QUnit.test("saveRecord does not load response if root key is missing", function( var record = Ember.run(RESTModel, RESTModel.create, {id: 1, name: "Erik", isNew: false}), responseData = {notRootKey: true}; + Ember.setOwner(record, Ember.getOwner(RESTModel)); + record.set('name', 'John'); assert.ok(record.get('isDirty'), 'Record should be dirty'); diff --git a/packages/ember-model/tests/belongs_to_test.js b/packages/ember-model/tests/belongs_to_test.js index 3ac2bdf..d99eab2 100644 --- a/packages/ember-model/tests/belongs_to_test.js +++ b/packages/ember-model/tests/belongs_to_test.js @@ -39,12 +39,21 @@ QUnit.test("model can be specified with a string instead of a class", function(a slug: Ember.attr(String) }), Comment = Ember.Model.extend({ - article: Ember.belongsTo('Ember.ArticleModel', { key: 'article', embedded: true }) + article: Ember.belongsTo('article', { key: 'article', embedded: true }) }); Article.primaryKey = 'slug'; - var comment = Comment.create(); + var owner = buildOwner(); + Ember.setOwner(Article, owner); + Ember.setOwner(Comment, owner); + + owner.register('model:article', Article); + owner.register('model:comment', Comment); + owner.register('service:store', Ember.Model.Store); + + var comment = Comment.create(owner.ownerInjection()); + Ember.run(comment, comment.load, 1, { article: { slug: 'first-article' } }); var article = Ember.run(comment, comment.get, 'article'); @@ -56,7 +65,6 @@ QUnit.test("model can be specified with a string to a resolved path", function(a var App; Ember.run(function() { App = Ember.Application.create({}); - App.register('emstore:main', Ember.Model.Store); }); App.Article = Ember.Model.extend({ id: Ember.attr(String) @@ -65,9 +73,7 @@ QUnit.test("model can be specified with a string to a resolved path", function(a article: Ember.belongsTo('article', { key: 'article', embedded: true }) }); - var store = App.__container__.lookup('emstore:main'); - var comment = store.createRecord('comment', {}); - + var comment = App.Comment.create({container: App.__container__}); Ember.run(comment, comment.load, 1, { article: { id: 'a' } }); var article = Ember.run(comment, comment.get, 'article'); @@ -77,23 +83,28 @@ QUnit.test("model can be specified with a string to a resolved path", function(a }); QUnit.test("non embedded belongsTo should get a record by its id", function(assert) { - var done = assert.async(); - var Article = Ember.Model.extend({ slug: Ember.attr(String) }), Comment = Ember.Model.extend({ - article: Ember.belongsTo(Article, { key: 'article_slug' }) + article: Ember.belongsTo('article', { key: 'article_slug' }) }); Article.primaryKey = 'slug'; Article.adapter = Ember.FixtureAdapter.create(); Article.FIXTURES = [{ slug: 'first-article' }]; + owner.register('model:article', Article); + owner.register('model:comment', Comment); + owner.register('service:store', Ember.Model.Store); + var comment = Comment.create(); + Ember.setOwner(comment, owner); + Ember.run(comment, comment.load, 1, { article_slug: 'first-article' }); var article = Ember.run(comment, comment.get, 'article'); + var done = assert.async(); article.one('didLoad', function() { assert.equal(article.get('slug'), 'first-article'); assert.ok(article instanceof Article); @@ -102,27 +113,36 @@ QUnit.test("non embedded belongsTo should get a record by its id", function(asse }); QUnit.test("relationship should be refreshed when data changes", function(assert) { - var done = assert.async(); - var Article = Ember.Model.extend({ slug: Ember.attr(String) }), Comment = Ember.Model.extend({ - article: Ember.belongsTo(Article, { key: 'article_slug' }) + article: Ember.belongsTo('article', { key: 'article_slug' }) }); + var owner = buildOwner(); + Ember.setOwner(Article, owner); + Ember.setOwner(Comment, owner); + + owner.register('model:article', Article); + owner.register('model:comment', Comment); + owner.register('service:store', Ember.Model.Store); + Article.primaryKey = 'slug'; Article.adapter = Ember.FixtureAdapter.create(); Article.FIXTURES = [{ slug: 'first-article' }]; var comment = Comment.create(); - var article = Ember.run(comment, comment.get, 'article'); + Ember.setOwner(comment, owner); + assert.ok(!article, "belongsTo relationship should default to null if there is no primaryKey defined"); + var article = Ember.run(comment, comment.get, 'article'); assert.ok(!article, "belongsTo relationship should default to null if there is no primaryKey defined"); Ember.run(comment, comment.load, 1, { article_slug: 'first-article' }); article = Ember.run(comment, comment.get, 'article'); + var done = assert.async(); article.one('didLoad', function() { assert.equal(article.get('slug'), 'first-article'); assert.ok(article instanceof Article); @@ -137,12 +157,19 @@ QUnit.test("when fetching an association getBelongsTo is called", function(asser slug: Ember.attr(String) }), Comment = Ember.Model.extend({ - article: Ember.belongsTo(Article, { key: 'article_slug' }) + article: Ember.belongsTo('article', { key: 'article_slug' }) }); Article.primaryKey = 'slug'; - var comment = Comment.create(); + var owner = buildOwner(); + Ember.setOwner(Article, owner); + Ember.setOwner(Comment, owner); + owner.register('model:article', Article); + owner.register('model:comment', Comment); + owner.register('service:store', Ember.Model.Store); + var comment = Comment.create(owner.ownerInjection()); + Ember.run(comment, comment.load, 1, { article_slug: 'first-article' }); comment.getBelongsTo = function(key, type, meta) { @@ -168,10 +195,16 @@ QUnit.test("toJSON uses the given relationship key in belongsTo", function(asser Article.FIXTURES = [{ token: 2 }]; var Comment = Ember.Model.extend({ - article: Ember.belongsTo(Article, { key: 'article_id' }) + article: Ember.belongsTo('article', { key: 'article_id' }) }); + var owner = buildOwner(); + Ember.setOwner(Article, owner); + Ember.setOwner(Comment, owner); + owner.register('model:article', Article); + owner.register('model:comment', Comment); + owner.register('service:store', Ember.Model.Store); - var comment = Comment.create(); + var comment = Comment.create(owner.ownerInjection()); Ember.run(comment, comment.load, 1, { article_id: 2 }); @@ -192,8 +225,10 @@ QUnit.test("un-embedded belongsTo CP should handle set", function(assert) { Post.adapter = Ember.FixtureAdapter.create(); Author.adapter = Ember.FixtureAdapter.create(); - var post = Post.create(), - author = Author.create(); + var owner = buildOwner(); + + var post = Post.create(owner.ownerInjection()), + author = Author.create(owner.ownerInjection()); Ember.run(function() { author.load(100, {id: 100}); @@ -205,6 +240,7 @@ QUnit.test("un-embedded belongsTo CP should handle set", function(assert) { }); assert.deepEqual(post.toJSON(), {id: 1, author_id: 100}); + }); QUnit.test("embedded belongsTo CP should handle set", function(assert) { @@ -304,7 +340,11 @@ QUnit.test("should be able to set embedded relationship to null", function(asser Comment.adapter = Ember.FixtureAdapter.create(); - var comment = Comment.create(); + var owner = buildOwner(); + Ember.setOwner(Article, owner); + Ember.setOwner(Comment, owner); + + var comment = Comment.create(owner.ownerInjection()); Ember.run(comment, comment.load, 1, { article: null }); assert.equal(comment.get('article'), null); // Materialize the data. @@ -460,21 +500,28 @@ QUnit.test("setting existing nonembedded relationship to NULL should make parent QUnit.test("relationships should be seralized when specified with string", function(assert) { assert.expect(1); - Ember.Author = Ember.Model.extend({ + var Author = Ember.Model.extend({ id: Ember.attr(), name: Ember.attr() }); - Ember.Post = Ember.Model.extend({ + var Post = Ember.Model.extend({ id: Ember.attr(), - author: Ember.belongsTo('Ember.Author', {key: 'author_id'}) + author: Ember.belongsTo('author', {key: 'author_id'}) }); - Ember.Post.adapter = Ember.FixtureAdapter.create(); - Ember.Author.adapter = Ember.FixtureAdapter.create(); + Post.adapter = Ember.FixtureAdapter.create(); + Author.adapter = Ember.FixtureAdapter.create(); + + var owner = buildOwner(); + Ember.setOwner(Author, owner); + Ember.setOwner(Post, owner); + owner.register('model:author', Author); + owner.register('model:post', Post); + owner.register('service:store', Ember.Model.Store); - var post = Ember.Post.create(), - author = Ember.Author.create(); + var post = Post.create(owner.ownerInjection()), + author = Author.create(owner.ownerInjection()); Ember.run(function() { author.load(100, {id: 100, name: 'bob'}); @@ -486,23 +533,33 @@ QUnit.test("relationships should be seralized when specified with string", funct QUnit.test("belongsTo from an embedded source is able to materialize without having to re-find", function(assert) { + + var Company = Ember.Company = Ember.Model.extend({ id: Ember.attr('string'), title: Ember.attr('string'), - projects: Ember.hasMany('Ember.Project', {key:'projects', embedded: true}) + projects: Ember.hasMany('project', {key:'projects', embedded: true}) }), Project = Ember.Project = Ember.Model.extend({ id: Ember.attr('string'), title: Ember.attr('string'), - posts: Ember.hasMany('Ember.Post', {key: 'posts', embedded: true}), - company: Ember.belongsTo('Ember.Company', {key:'company'}) + posts: Ember.hasMany('post', {key: 'posts', embedded: true}), + company: Ember.belongsTo('company', {key:'company'}) }), Post = Ember.Post = Ember.Model.extend({ id: Ember.attr('string'), title: Ember.attr('string'), body: Ember.attr('string'), - project: Ember.belongsTo('Ember.Project', {key:'project'}) + project: Ember.belongsTo('project', {key:'project'}) }); + var owner = buildOwner(); + Ember.setOwner(Company, owner); + Ember.setOwner(Project, owner); + Ember.setOwner(Post, owner); + owner.register('model:company', Company); + owner.register('model:project', Project); + owner.register('model:post', Post); + owner.register('service:store', Ember.Model.Store); var compJson = { id:1, @@ -529,18 +586,27 @@ QUnit.test("belongsTo from an embedded source is able to materialize without hav assert.equal(project1, post1.get('project')); }); + QUnit.test("unloaded records are removed from reference cache", function(assert) { + var Company = Ember.Company = Ember.Model.extend({ id: Ember.attr('string'), title: Ember.attr('string'), - projects: Ember.hasMany('Ember.Project', {key:'projects', embedded: true}) + projects: Ember.hasMany('project', {key:'projects', embedded: true}) }), Project = Ember.Project = Ember.Model.extend({ id: Ember.attr('string'), title: Ember.attr('string'), - company: Ember.belongsTo('Ember.Company', {key:'company'}) + company: Ember.belongsTo('company', {key:'company'}) }); + var owner = buildOwner(); + Ember.setOwner(Company, owner); + Ember.setOwner(Project, owner); + owner.register('model:company', Company); + owner.register('model:project', Project); + owner.register('service:store', Ember.Model.Store); + var compJson = { id:1, title:'coolio', @@ -568,6 +634,7 @@ QUnit.test("unloaded records are removed from reference cache", function(assert) company = Company.find(1); var reloadedProject1 = company.get('projects.firstObject'); + assert.notEqual(project1, reloadedProject1); assert.equal(project1.get('title'), 'project one title'); assert.equal(reloadedProject1.get('title'), 'project one new title'); @@ -577,14 +644,21 @@ QUnit.test("unloaded records are removed from hasMany cache", function(assert) { var Company = Ember.Company = Ember.Model.extend({ id: Ember.attr('string'), title: Ember.attr('string'), - projects: Ember.hasMany('Ember.Project', {key:'projects', embedded: true}) + projects: Ember.hasMany('project', {key:'projects', embedded: true}) }), Project = Ember.Project = Ember.Model.extend({ id: Ember.attr('string'), title: Ember.attr('string'), - company: Ember.belongsTo('Ember.Company', {key:'company'}) + company: Ember.belongsTo('company', {key:'company'}) }); + var owner = buildOwner(); + Ember.setOwner(Company, owner); + Ember.setOwner(Project, owner); + owner.register('model:company', Company); + owner.register('model:project', Project); + owner.register('service:store', Ember.Model.Store); + var compJson = { id:1, title:'coolio', @@ -617,17 +691,25 @@ QUnit.test("unloaded records are removed from hasMany cache", function(assert) { }); QUnit.test("belongsTo records created are available from reference cache", function(assert) { + var Company = Ember.Company = Ember.Model.extend({ id: Ember.attr('string'), title: Ember.attr('string'), - project: Ember.belongsTo('Ember.Project', {key:'project', embedded: true}) + project: Ember.belongsTo('project', {key:'project', embedded: true}) }), Project = Ember.Project = Ember.Model.extend({ id: Ember.attr('string'), title: Ember.attr('string'), - company: Ember.belongsTo('Ember.Company', {key:'company'}) + company: Ember.belongsTo('company', {key:'company'}) }); + var owner = buildOwner(); + Ember.setOwner(Company, owner); + Ember.setOwner(Project, owner); + owner.register('model:company', Company); + owner.register('model:project', Project); + owner.register('service:store', Ember.Model.Store); + var compJson = { id:1, title:'coolio', @@ -687,21 +769,27 @@ QUnit.test("key defaults to model's property key", function(assert) { Article.FIXTURES = [{ id: 2 }]; var Comment = Ember.Model.extend({ - article: Ember.belongsTo(Article) + article: Ember.belongsTo('article') }); - var comment = Comment.create(); + var owner = buildOwner(); + Ember.setOwner(Article, owner); + Ember.setOwner(Comment, owner); + + var comment = Comment.create(owner.ownerInjection()); + owner.register('model:article', Article); + owner.register('model:comment', Comment); + owner.register('service:store', Ember.Model.Store); Ember.run(comment, comment.load, 1, { article: 2 }); assert.deepEqual(comment.toJSON(), { article: 2 }); }); -QUnit.test("non embedded belongsTo should return a record with an owner", function(assert) { +QUnit.test("non embedded belongsTo should return a record with a container", function(assert) { var App; Ember.run(function() { App = Ember.Application.create({}); - App.register('emstore:main', Ember.Model.Store); }); App.Article = Ember.Model.extend({ id: Ember.attr(String) @@ -713,11 +801,9 @@ QUnit.test("non embedded belongsTo should return a record with an owner", functi App.Article.adapter = Ember.FixtureAdapter.create(); App.Article.FIXTURES = [{ id: 'first-article' }]; - var store = App.__container__.lookup('emstore:main'); - var comment = store.createRecord('comment', {}); - + var comment = App.Comment.create({container: App.__container__}); Ember.run(comment, comment.load, 1, { article_slug: 'first-article' }); var article = Ember.run(comment, comment.get, 'article'); - assert.ok(Ember.getOwner(article)); + assert.ok(article.get('container')); Ember.run(App, 'destroy'); }); diff --git a/packages/ember-model/tests/dirty_tracking_test.js b/packages/ember-model/tests/dirty_tracking_test.js index 1c57656..939a393 100644 --- a/packages/ember-model/tests/dirty_tracking_test.js +++ b/packages/ember-model/tests/dirty_tracking_test.js @@ -13,7 +13,9 @@ QUnit.test("when no properties have changed on a model, save should noop", funct } }; - var obj = Ember.run(Model, Model.create, {isNew: false}); + var owner = buildOwner(); + + var obj = Ember.run(Model, Model.create, owner.ownerInjection(),{isNew: false}); assert.ok(!obj.get('isDirty')); Ember.run(obj, obj.save); @@ -34,7 +36,9 @@ QUnit.test("when properties have changed on a model, isDirty should be set", fun } }; - var obj = Ember.run(Model, Model.create, {isNew: false}); + var owner = buildOwner(); + Ember.setOwner(Model, owner); + var obj = Ember.run(Model, Model.create, owner.ownerInjection(), {isNew: false}); assert.ok(!obj.get('isDirty')); obj.set('name', 'Jeffrey'); @@ -109,7 +113,9 @@ QUnit.test("after saving, the model shouldn't be dirty", function(assert) { } }; - var obj = Ember.run(Model, Model.create, {isNew: false}); + var owner = buildOwner(); + Ember.setOwner(Model, owner); + var obj = Ember.run(Model, Model.create, owner.ownerInjection(), {isNew: false}); obj.set('name', 'Erik'); assert.ok(obj.get('isDirty')); @@ -264,7 +270,10 @@ QUnit.test("manipulating object presence in a hasMany should dirty the parent", }); Post.adapter = Ember.FixtureAdapter.create(); - var post = Post.create({isNew: false, _data: {comments: []}}); + var owner = buildOwner(); + Ember.setOwner(Comment, owner); + Ember.setOwner(Post, owner); + var post = Post.create(owner.ownerInjection(), {isNew: false, _data: {comments: []}}); assert.ok(!post.get('isDirty'), "Post should be clean initially"); @@ -439,7 +448,10 @@ QUnit.test("isDirty on embedded hasMany records should be false after parent is comments: Ember.hasMany(Comment, {key: 'comments', embedded: true}) }); - var post = Post.create({ + var owner = buildOwner(); + Ember.setOwner(Comment, owner); + Ember.setOwner(Post, owner); + var post = Post.create(owner.ownerInjection(), { isNew: false, _data: { comments: [{body: "The body"}] @@ -555,7 +567,15 @@ QUnit.test("save parent of embedded belongsTo", function(assert) { Post.adapter = Ember.FixtureAdapter.create(); - var post = Post.create(); + var owner = buildOwner(); + Ember.setOwner(Author, owner); + Ember.setOwner(Post, owner); + owner.register('model:author', Author); + owner.register('model:post', Post); + owner.register('service:store', Ember.Model.Store); + + var post = Post.create(owner.ownerInjection()); + Ember.run(post, post.load, json.id, json); assert.equal(post.get('isDirty'), false, 'post should be clean initially'); @@ -600,7 +620,15 @@ QUnit.test("save parent of embedded belongsTo with different named key", functio Post.adapter = Ember.FixtureAdapter.create(); - var post = Post.create(); + var owner = buildOwner(); + Ember.setOwner(Author, owner); + Ember.setOwner(Post, owner); + owner.register('model:author', Author); + owner.register('model:post', Post); + owner.register('service:store', Ember.Model.Store); + + var post = Post.create(owner.ownerInjection()); + Ember.run(post, post.load, json.id, json); assert.equal(post.get('isDirty'), false, 'post should be clean initially'); @@ -640,12 +668,22 @@ QUnit.test("set embedded belongsTo", function(assert) { }), Post = Ember.Model.extend({ id: Ember.attr(), - author: Ember.belongsTo(Author, {key: 'author', embedded: true}) + author: Ember.belongsTo('author', {key: 'author', embedded: true}) }); Post.adapter = Ember.FixtureAdapter.create(); - var post = Post.create(); + var owner = buildOwner(); + + Ember.setOwner(Author, owner); + Ember.setOwner(Post, owner); + + owner.register('model:author', Author); + owner.register('model:post', Post); + owner.register('service:store', Ember.Model.Store); + + var post = Post.create(owner.ownerInjection()); + Ember.run(post, post.load, json.id, json); assert.equal(post.get('isDirty'), false, 'post should be clean initially'); @@ -732,7 +770,11 @@ QUnit.test("manipulating the content of objects in a hasMany should dirty the pa comments: Ember.hasMany(Comment, {key: 'comments', embedded: true}) }); - var post = Post.create({ + owner = buildOwner(); + Ember.setOwner(Comment, owner); + Ember.setOwner(Post, owner); + + var post = Post.create(owner.ownerInjection(), { isNew: false, _data: { comments: [json] } }); diff --git a/packages/ember-model/tests/filtered_record_array_test.js b/packages/ember-model/tests/filtered_record_array_test.js index b1cd10d..26bb22d 100644 --- a/packages/ember-model/tests/filtered_record_array_test.js +++ b/packages/ember-model/tests/filtered_record_array_test.js @@ -1,3 +1,5 @@ +import { start } from "repl"; + var Model; QUnit.module("Ember.FilteredRecordArray", { @@ -30,7 +32,7 @@ QUnit.test("must be created with a filterFunction property", function(assert) { QUnit.test("must be created with a filterProperties property", function(assert) { assert.throws(function() { - Ember.FilteredRecordArray.create({modelClass: Model, filterFunction: function() {} }); + Ember.FilteredRecordArray.create({modelClass: Model, filterFunction: () => true }); }, /FilteredRecordArrays must be created with filterProperties/); }); @@ -83,10 +85,10 @@ QUnit.test("loading a record that doesn't match the filter after creating a Filt filterProperties: ['name'] }); - Model.create({id: 3, name: 'Kris'}).save().then(function(record) { + Model.create(owner.ownerInjection(), {id: 3, name: 'Kris'}).save().then(function(record) { + start(); assert.equal(recordArray.get('length'), 1, "There is still 1 record"); assert.equal(recordArray.get('firstObject.name'), 'Erik', "The record data matches"); - done(); }); }); @@ -105,11 +107,11 @@ QUnit.test("loading a record that matches the filter after creating a FilteredRe filterProperties: ['name'] }); - Model.create({id: 3, name: 'Kris'}).save().then(function(record) { + Model.create(owner.ownerInjection(), {id: 3, name: 'Kris'}).save().then(function(record) { + start(); assert.equal(recordArray.get('length'), 2, "There are 2 records"); assert.equal(recordArray.get('firstObject.name'), 'Erik', "The record data matches"); assert.equal(recordArray.get('lastObject.name'), 'Kris', "The record data matches"); - done(); }); }); }); @@ -156,7 +158,8 @@ QUnit.test("adding a new record and changing a property that matches the filter assert.equal(recordArray.get('length'), 1, "There is 1 record initially"); assert.equal(recordArray.get('firstObject.name'), 'Erik', "The record data matches"); - Model.create({id: 3, name: 'Kris'}).save().then(function(record) { + Model.create(owner.ownerInjection(), {id: 3, name: 'Kris'}).save().then(function(record) { + start(); record.set('name', 'Ekris'); assert.equal(recordArray.get('length'), 2, "There are 2 records after changing the name"); @@ -171,4 +174,6 @@ QUnit.test("adding a new record and changing a property that matches the filter done(); }); }); + + stop(); }); diff --git a/packages/ember-model/tests/has_many/embedded_objects_load_test.js b/packages/ember-model/tests/has_many/embedded_objects_load_test.js index 5b36281..4db7039 100644 --- a/packages/ember-model/tests/has_many/embedded_objects_load_test.js +++ b/packages/ember-model/tests/has_many/embedded_objects_load_test.js @@ -21,9 +21,16 @@ QUnit.test("derp", function(assert) { var Article = Ember.Model.extend({ title: attr(), - comments: Ember.hasMany(Comment, { key: 'comments', embedded: true }) + comments: Ember.hasMany('comment', { key: 'comments', embedded: true }) }); + var owner = buildOwner(); + Ember.setOwner(Comment, owner); + Ember.setOwner(Article, owner); + owner.register('model:comment', Comment); + owner.register('model:article', Article); + owner.register('service:store', Ember.Model.Store); + var article = Article.create(); Ember.run(article, article.load, json.id, json); @@ -58,9 +65,16 @@ QUnit.test("loading embedded data into a parent updates the child records", func var Post = Ember.Model.extend({ id: attr(), - comments: Ember.hasMany(Comment, {key: 'comments', embedded: true}) + comments: Ember.hasMany('comment', {key: 'comments', embedded: true}) }); + var owner = buildOwner(); + Ember.setOwner(Comment, owner); + Ember.setOwner(Article, owner); + owner.register('model:comment', Comment); + owner.register('model:article', Article); + owner.register('service:store', Ember.Model.Store); + Post.adapter = { find: function(record, id) { record.load(id, {comments: []}); diff --git a/packages/ember-model/tests/has_many/embedded_objects_save_test.js b/packages/ember-model/tests/has_many/embedded_objects_save_test.js index caa5506..43d3962 100644 --- a/packages/ember-model/tests/has_many/embedded_objects_save_test.js +++ b/packages/ember-model/tests/has_many/embedded_objects_save_test.js @@ -92,12 +92,13 @@ QUnit.test("new records should remain after parent is saved", function(assert) { resolve(json); }); }; - - var article = Article.create({ + + var owner = buildOwner(); + var article = Article.create(owner.ownerInjection(), { title: 'foo' }); - var comment = Comment.create({ + var comment = Comment.create(owner.ownerInjection(), { text: 'comment text' }); article.get('comments').addObject(comment); diff --git a/packages/ember-model/tests/has_many/nonembedded_objects_save_test.js b/packages/ember-model/tests/has_many/nonembedded_objects_save_test.js index 1987e18..2044c6a 100644 --- a/packages/ember-model/tests/has_many/nonembedded_objects_save_test.js +++ b/packages/ember-model/tests/has_many/nonembedded_objects_save_test.js @@ -20,7 +20,7 @@ QUnit.test("new records should remain after parent is saved", function(assert) { var Article = Ember.Model.extend({ id: attr(), title: attr(), - comments: Ember.hasMany(Comment, { key: 'comment_ids' }) + comments: Ember.hasMany('comment', { key: 'comment_ids' }) }); Article.adapter = Ember.RESTAdapter.create(); Article.url = '/articles'; @@ -30,11 +30,17 @@ QUnit.test("new records should remain after parent is saved", function(assert) { }); }; - var article = Article.create({ + var owner = buildOwner(); + Ember.setOwner(Comment, owner); + Ember.setOwner(Article, owner); + owner.register('service:store', Ember.Model.Store); + owner.register('model:comment', Comment); + owner.register('model:article', Article); + var article = Article.create(owner.ownerInjection(), { title: 'bar' }); - var comment = Comment.create({ + var comment = Comment.create(owner.ownerInjection(), { text: 'comment text' }); @@ -73,13 +79,20 @@ QUnit.test("saving child objects", function(assert) { var Article = Ember.Model.extend({ id: attr(), title: attr(), - comments: Ember.hasMany(Comment, { key: 'comment_ids' }) + comments: Ember.hasMany('comment', { key: 'comment_ids' }) }); Article.adapter = Ember.RESTAdapter.create(); Article.url = '/articles'; - var article = Article.create(); - var comment = Comment.create(); + var owner = buildOwner(); + Ember.setOwner(Comment, owner); + Ember.setOwner(Article, owner); + owner.register('service:store', Ember.Model.Store); + owner.register('model:comment', Comment); + owner.register('model:article', Article); + + var article = Article.create(owner.ownerInjection()); + var comment = Comment.create(owner.ownerInjection()); var comments = article.get("comments"); comments.addObject(comment); diff --git a/packages/ember-model/tests/has_many_test.js b/packages/ember-model/tests/has_many_test.js index 57a32e3..591f694 100644 --- a/packages/ember-model/tests/has_many_test.js +++ b/packages/ember-model/tests/has_many_test.js @@ -69,15 +69,22 @@ QUnit.test("using it in a model definition", function(assert) { QUnit.test("model can be specified with a string instead of a class", function(assert) { var Article = Ember.Model.extend({ - comments: Ember.hasMany('Ember.CommentModel', { key: 'comments', embedded: true }) + comments: Ember.hasMany('Ecomment', { key: 'comments', embedded: true }) }), - Comment = Ember.CommentModel = Ember.Model.extend({ + Comment = Ember.Model.extend({ token: Ember.attr(String) }); + var owner = buildOwner(); + Ember.setOwner(Comment, owner); + Ember.setOwner(Article, owner); + owner.register('model:article', Article); + owner.register('model:comment', Comment); + owner.register('service:store', Ember.Model.Store); + Comment.primaryKey = 'token'; - var article = Article.create(); + var article = Article.create(owner.ownerInjection()); Ember.run(article, article.load, 1, {comments: Ember.A([{token: 'a'}, {token: 'b'}])}); assert.equal(article.get('comments.length'), 2); @@ -246,20 +253,30 @@ QUnit.test("has many records created are available from reference cache", functi var Company = Ember.Company = Ember.Model.extend({ id: Ember.attr('string'), title: Ember.attr('string'), - projects: Ember.hasMany('Ember.Project', {key:'projects', embedded: true}) - }), - Project = Ember.Project = Ember.Model.extend({ - id: Ember.attr('string'), - title: Ember.attr('string'), - posts: Ember.hasMany('Ember.Post', {key: 'posts', embedded: true}), - company: Ember.belongsTo('Ember.Company', {key:'company'}) - }), - Post = Ember.Post = Ember.Model.extend({ - id: Ember.attr('string'), - title: Ember.attr('string'), - body: Ember.attr('string'), - project: Ember.belongsTo('Ember.Project', {key:'project'}) - }); + projects: Ember.hasMany('project', {key:'projects', embedded: true}) + }); + var Project = Ember.Project = Ember.Model.extend({ + id: Ember.attr('string'), + title: Ember.attr('string'), + posts: Ember.hasMany('post', {key: 'posts', embedded: true}), + company: Ember.belongsTo('company', {key:'company'}) + }); + var Post = Ember.Post = Ember.Model.extend({ + id: Ember.attr('string'), + title: Ember.attr('string'), + body: Ember.attr('string'), + project: Ember.belongsTo('project', {key:'project'}) + }); + + var owner = buildOwner(); + Ember.setOwner(Company, owner); + Ember.setOwner(Project, owner); + Ember.setOwner(Post, owner); + + owner.register('model:company', Company); + owner.register('model:project', Project); + owner.register('model:post', Post); + owner.register('service:store', Ember.Model.Store); var compJson = { id:1, diff --git a/packages/ember-model/tests/model_test.js b/packages/ember-model/tests/model_test.js index 1b3e6e4..6e46389 100644 --- a/packages/ember-model/tests/model_test.js +++ b/packages/ember-model/tests/model_test.js @@ -104,6 +104,12 @@ QUnit.test("can handle models with ID of zero", function(assert) { name: Ember.attr() }); + ModelWithZeroID.reopenClass({ + primaryKey: 'id' + }); + + Ember.setOwner(ModelWithZeroID, owner); + ModelWithZeroID.adapter = Ember.FixtureAdapter.create(); ModelWithZeroID.FIXTURES = [ { id: 0, name: 'Erik' } @@ -284,7 +290,7 @@ QUnit.test("new records are added to the identity map", function(assert) { assert.expect(2); var done = assert.async(); - var record = Model.create({token: 2, name: 'Yehuda'}); + var record = Model.create(owner.ownerInjection(), {token: 2, name: 'Yehuda'}); record.save(); @@ -300,7 +306,7 @@ QUnit.test("creating a new record adds it to existing record arrays", function(a var done = assert.async(); var records = Model.find(); - var record = Model.create({token: 'b', name: 'Yehuda'}); + var record = Model.create(owner.ownerInjection(), {token: 'b', name: 'Yehuda'}); record.save(); record.on('didSaveRecord', function() { @@ -329,7 +335,7 @@ QUnit.test("record isNew & isSaving flags", function(assert) { assert.expect(5); var done = assert.async(); - var record = Model.create(); + var record = Model.create(owner.ownerInjection()); assert.ok(record.get('isNew')); record.save(); @@ -457,7 +463,7 @@ QUnit.test("Model#create() works as expected", function(assert) { assert.expect(10); var done = assert.async(); - var record = Model.create({name: 'Yehuda'}); + var record = Model.create(owner.ownerInjection(), {name: 'Yehuda'}); assert.ok(record.get('isNew'), "record isNew upon instantiation"); assert.ok(record.get('isLoaded'), "record isLoaded upon instantiation"); @@ -476,7 +482,7 @@ QUnit.test("Model#create() works as expected", function(assert) { assert.ok(record.get('isSaving'), 'The record isSaving flag is true while saving is in progress'); }); -QUnit.test(".getAttributes() returns the model's attributes", function(assert) { +QUnit.test(".attributes returns the model's attributes", function(assert) { var attr = Ember.attr, BaseModel = Ember.Model.extend({ id: attr() @@ -498,13 +504,13 @@ QUnit.test(".getAttributes() returns the model's attributes", function(assert) { species: attr() }); - assert.deepEqual(Employee.getAttributes(), ['id', 'name', 'nationality', 'employeeId']); - assert.deepEqual(Person.getAttributes(), ['id', 'name', 'nationality']); - assert.deepEqual(Animal.getAttributes(), ['id', 'order', 'family', 'genus', 'species']); - assert.deepEqual(BaseModel.getAttributes(), ['id']); + assert.deepEqual(Array.from(Employee.attributes.keys()).sort(), ['id', 'name', 'nationality', 'employeeId'].sort()); + assert.deepEqual(Array.from(Person.attributes.keys()).sort(), ['id', 'name', 'nationality'].sort()); + assert.deepEqual(Array.from(Animal.attributes.keys()).sort(), ['id', 'order', 'family', 'genus', 'species'].sort()); + assert.deepEqual(Array.from(BaseModel.attributes.keys()), ['id']); }); -QUnit.test(".getRelationships() returns the model's relationships", function(assert) { +QUnit.test(".relationships returns the model's relationships", function(assert) { var Comment = Ember.Model.extend(), Rating = Ember.Model.extend(), Author = Ember.Model.extend(), @@ -523,9 +529,9 @@ QUnit.test(".getRelationships() returns the model's relationships", function(ass source: Ember.belongsTo(Site, { key: 'site' }) }); - assert.deepEqual(Commentable.getRelationships(), ['comments']); - assert.deepEqual(Article.getRelationships(), ['comments', 'author', 'ratings']); - assert.deepEqual(News.getRelationships(), ['comments', 'author', 'ratings', 'source']); + assert.deepEqual(Array.from(Commentable.relationships.keys()).sort(), ['comments'].sort()); + assert.deepEqual(Array.from(Article.relationships.keys()).sort(), ['comments', 'author', 'ratings'].sort()); + assert.deepEqual(Array.from(News.relationships.keys()).sort(), ['comments', 'author', 'ratings', 'source'].sort()); }); QUnit.test("toJSON includes embedded relationships", function(assert) { @@ -577,8 +583,9 @@ QUnit.test("toJSON includes non-embedded relationships", function(assert) { Article = Ember.Model.extend({ id: 1, title: Ember.attr(), - comments: Ember.hasMany(Comment, { key: 'comments' }), - author: Ember.belongsTo(Author, { key: 'author' }) + type: 'test', + comments: Ember.hasMany('comment', { key: 'comments' }), + author: Ember.belongsTo('author', { key: 'author' }) }); var articleData = { @@ -587,6 +594,16 @@ QUnit.test("toJSON includes non-embedded relationships", function(assert) { comments: [1, 2, 3], author: 1 }; + owner = buildOwner(); + store = Ember.Model.Store.create(); + Ember.setOwner(store, owner); + Ember.setOwner(Comment, owner); + Ember.setOwner(Author, owner); + Ember.setOwner(Article, owner); + owner.register('model:comment', Comment); + owner.register('model:author', Author); + owner.register('model:article', Article); + owner.register('service:store', Ember.Model.Store); Author.adapter = Ember.FixtureAdapter.create(); Comment.adapter = Ember.FixtureAdapter.create(); @@ -600,6 +617,7 @@ QUnit.test("toJSON includes non-embedded relationships", function(assert) { var article = Article.create(); + Ember.setOwner(article, owner); Ember.run(article, article.load, articleData.id, articleData); var json = Ember.run(article, article.toJSON); diff --git a/packages/ember-model/tests/record_array_test.js b/packages/ember-model/tests/record_array_test.js index 639af1c..dbb6cfc 100644 --- a/packages/ember-model/tests/record_array_test.js +++ b/packages/ember-model/tests/record_array_test.js @@ -18,6 +18,32 @@ QUnit.module("Ember.RecordArray", { {id: 2, name: 'Stefan'}, {id: 3, name: 'Kris'} ]; + owner = buildOwner(); + store = Ember.Model.Store.create(); + Ember.setOwner(store, owner); + Ember.setOwner(Model, owner); + owner.register('model:test', Model); + owner.register('service:store', Ember.Model.Store); + data = [ + {id: 1, name: 'Erik'}, + {id: 2, name: 'Aaron'} + ]; + RESTModel = Ember.Model.extend({ + id: Ember.attr(), + name: Ember.attr(), + type: 'test' + }); + var adapter = Ember.RESTAdapter.create(); + adapter._ajax = function(url, params, method) { + return ajaxSuccess(data); + }; + adapter.findMany = function(klass, records, ids) { + return adapter.findAll(klass, records); + }; + RESTModel.adapter = adapter; + RESTModel.url = '/fake/api'; + Ember.setOwner(RESTModel, owner); + owner.register('model:test', RESTModel); } }); @@ -238,6 +264,7 @@ QUnit.test("RecordArray handles already inserted new models being saved", functi assert.equal(records.get('length'), 1); var newModel = RESTModel.create(); + Ember.setOwner(newModel, Ember.getOwner(RESTModel)); records.pushObject(newModel);