Skip to content

Commit

Permalink
Refactor internals to use Javascript objects for internals instead of…
Browse files Browse the repository at this point in the history
… DS.Model

This commit adds an `InternalModel` class that is now used everywhere inside ED
to represent models. This class is a fast Javascript object that contains all the data
that we know for a particular record. At the ED/App code boundaries, such as responses
to `find`, models being set/pushed to `belongsTo`/`hasMany` we convert between
internalModels and DS.Models.

This should be a huge performance win, because we now lazily create DS.Models which are
pretty slow to instantiate.

We need to wait for the new serializer/push refactor in order to use a `push` that does
not immediately materialize a record to get further perf gains. For now most of perf gains
if for foreign keys.
  • Loading branch information
igorT committed Jun 1, 2015
1 parent 31f2f1c commit 57074a4
Show file tree
Hide file tree
Showing 28 changed files with 1,013 additions and 670 deletions.
13 changes: 9 additions & 4 deletions packages/ember-data/lib/system/many-array.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { PromiseArray } from "ember-data/system/promise-proxies";
var get = Ember.get;
var set = Ember.set;
var filter = Ember.ArrayPolyfills.filter;
var map = Ember.EnumerableUtils.map;

/**
A `ManyArray` is a `MutableArray` that represents the contents of a has-many
Expand Down Expand Up @@ -57,19 +58,23 @@ export default Ember.Object.extend(Ember.MutableArray, Ember.Evented, {
length: 0,

objectAt: function(index) {
return this.currentState[index];
//Ember observers such as 'firstObject', 'lastObject' might do out of bounds accesses
if (!this.currentState[index]) {
return undefined;
}
return this.currentState[index].getRecord();
},

flushCanonical: function() {
//TODO make this smarter, currently its plenty stupid
var toSet = filter.call(this.canonicalState, function(record) {
return !record.get('isDeleted');
return !record.isDeleted();
});

//a hack for not removing new records
//TODO remove once we have proper diffing
var newRecords = this.currentState.filter(function(record) {
return record.get('isNew');
return record.isNew();
});
toSet = toSet.concat(newRecords);
var oldLength = this.length;
Expand Down Expand Up @@ -143,7 +148,7 @@ export default Ember.Object.extend(Ember.MutableArray, Ember.Evented, {
this.get('relationship').removeRecords(records);
}
if (objects) {
this.get('relationship').addRecords(objects, idx);
this.get('relationship').addRecords(map(objects, function(obj) { return obj._internalModel; }), idx);
}
},
/**
Expand Down
14 changes: 8 additions & 6 deletions packages/ember-data/lib/system/model/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -300,25 +300,27 @@ export default function attr(type, options) {

return computedPolyfill({
get: function(key) {
if (hasValue(this, key)) {
return getValue(this, key);
var internalModel = this._internalModel;
if (hasValue(internalModel, key)) {
return getValue(internalModel, key);
} else {
return getDefaultValue(this, options, key);
}
},
set: function(key, value) {
Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('<type>')` from " + this.constructor.toString(), key !== 'id');
var oldValue = getValue(this, key);
var internalModel = this._internalModel;
var oldValue = getValue(internalModel, key);

if (value !== oldValue) {
// Add the new value to the changed attributes hash; it will get deleted by
// the 'didSetProperty' handler if it is no different from the original value
this._attributes[key] = value;
internalModel._attributes[key] = value;

this.send('didSetProperty', {
this._internalModel.send('didSetProperty', {
name: key,
oldValue: oldValue,
originalValue: this._data[key],
originalValue: internalModel._data[key],
value: value
});
}
Expand Down
Loading

0 comments on commit 57074a4

Please sign in to comment.