2
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2025-09-06 10:50:21 +08:00
discourse/app/assets/javascripts/discourse/models/store.js.es6
2015-04-27 13:52:54 -04:00

216 lines
6 KiB
JavaScript

import RestModel from 'discourse/models/rest';
import ResultSet from 'discourse/models/result-set';
let _identityMap;
// You should only call this if you're a test scaffold
function flushMap() {
_identityMap = {};
}
function storeMap(type, id, obj) {
if (!id) { return; }
_identityMap[type] = _identityMap[type] || {};
_identityMap[type][id] = obj;
}
function fromMap(type, id) {
const byType = _identityMap[type];
if (byType) { return byType[id]; }
}
function removeMap(type, id) {
const byType = _identityMap[type];
if (byType) { delete byType[id]; }
}
function findAndRemoveMap(type, id) {
const byType = _identityMap[type];
if (byType) {
const result = byType[id];
delete byType[id];
return result;
}
}
flushMap();
export default Ember.Object.extend({
pluralize(thing) {
return thing + "s";
},
findAll(type) {
const self = this;
return this.adapterFor(type).findAll(this, type).then(function(result) {
return self._resultSet(type, result);
});
},
// Mostly for legacy, things like TopicList without ResultSets
findFiltered(type, findArgs) {
const self = this;
return this.adapterFor(type).find(this, type, findArgs).then(function(result) {
return self._build(type, result);
});
},
find(type, findArgs) {
const self = this;
return this.adapterFor(type).find(this, type, findArgs).then(function(result) {
if (typeof findArgs === "object") {
return self._resultSet(type, result);
} else {
return self._hydrate(type, result[Ember.String.underscore(type)], result);
}
});
},
refreshResults(resultSet, type, url) {
const self = this;
return Discourse.ajax(url).then(function(result) {
const typeName = Ember.String.underscore(self.pluralize(type)),
content = result[typeName].map(obj => self._hydrate(type, obj, result));
resultSet.set('content', content);
});
},
appendResults(resultSet, type, url) {
const self = this;
return Discourse.ajax(url).then(function(result) {
const typeName = Ember.String.underscore(self.pluralize(type)),
totalRows = result["total_rows_" + typeName] || result.get('totalRows'),
loadMoreUrl = result["load_more_" + typeName],
content = result[typeName].map(obj => self._hydrate(type, obj, result));
resultSet.setProperties({ totalRows, loadMoreUrl });
resultSet.get('content').pushObjects(content);
// If we've loaded them all, clear the load more URL
if (resultSet.get('length') >= totalRows) {
resultSet.set('loadMoreUrl', null);
}
});
},
update(type, id, attrs) {
return this.adapterFor(type).update(this, type, id, attrs, function(result) {
if (result && result[type] && result[type].id) {
const oldRecord = findAndRemoveMap(type, id);
storeMap(type, result[type].id, oldRecord);
}
return result;
});
},
createRecord(type, attrs) {
attrs = attrs || {};
return !!attrs.id ? this._hydrate(type, attrs) : this._build(type, attrs);
},
destroyRecord(type, record) {
return this.adapterFor(type).destroyRecord(this, type, record).then(function(result) {
removeMap(type, record.get('id'));
return result;
});
},
_resultSet(type, result) {
const typeName = Ember.String.underscore(this.pluralize(type)),
content = result[typeName].map(obj => this._hydrate(type, obj, result)),
totalRows = result["total_rows_" + typeName] || content.length,
loadMoreUrl = result["load_more_" + typeName],
refreshUrl = result['refresh_' + typeName];
return ResultSet.create({ content, totalRows, loadMoreUrl, refreshUrl, store: this, __type: type });
},
_build(type, obj) {
obj.store = this;
obj.__type = type;
obj.__state = obj.id ? "created" : "new";
const klass = this.container.lookupFactory('model:' + type) || RestModel;
const model = klass.create(obj);
storeMap(type, obj.id, model);
return model;
},
adapterFor(type) {
return this.container.lookup('adapter:' + type) || this.container.lookup('adapter:rest');
},
_lookupSubType(subType, id, root) {
// cheat: we know we already have categories in memory
if (subType === 'category') {
return Discourse.Category.findById(id);
}
const pluralType = this.pluralize(subType);
const collection = root[this.pluralize(subType)];
if (collection) {
const hashedProp = "__hashed_" + pluralType;
let hashedCollection = root[hashedProp];
if (!hashedCollection) {
hashedCollection = {};
collection.forEach(function(it) {
hashedCollection[it.id] = it;
});
root[hashedProp] = hashedCollection;
}
const found = hashedCollection[id];
if (found) {
const hydrated = this._hydrate(subType, found, root);
hashedCollection[id] = hydrated;
return hydrated;
}
}
},
_hydrateEmbedded(obj, root) {
const self = this;
Object.keys(obj).forEach(function(k) {
const m = /(.+)\_id$/.exec(k);
if (m) {
const subType = m[1];
const hydrated = self._lookupSubType(subType, obj[k], root);
if (hydrated) {
obj[subType] = hydrated;
delete obj[k];
}
}
});
},
_hydrate(type, obj, root) {
if (!obj) { throw "Can't hydrate " + type + " of `null`"; }
if (!obj.id) { throw "Can't hydrate " + type + " without an `id`"; }
root = root || obj;
// Experimental: If serialized with a certain option we'll wire up embedded objects
// automatically.
if (root.__rest_serializer === "1") {
this._hydrateEmbedded(obj, root);
}
const existing = fromMap(type, obj.id);
if (existing === obj) { return existing; }
if (existing) {
delete obj.id;
const klass = this.container.lookupFactory('model:' + type) || RestModel;
existing.setProperties(klass.munge(obj));
return existing;
}
return this._build(type, obj);
}
});
export { flushMap };