Merge pull request #296 from fabien/feature/default-scope
Default scope
This commit is contained in:
commit
0859fc4c21
|
@ -70,6 +70,31 @@ function deserialize(dbObj) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Memory.prototype.getCollection = function(model) {
|
||||||
|
var modelClass = this._models[model];
|
||||||
|
if (modelClass.settings.memory) {
|
||||||
|
model = modelClass.settings.memory.collection || model;
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
Memory.prototype.initCollection = function(model) {
|
||||||
|
this.collection(model, {});
|
||||||
|
this.collectionSeq(model, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Memory.prototype.collection = function(model, val) {
|
||||||
|
model = this.getCollection(model);
|
||||||
|
if (arguments.length > 1) this.cache[model] = val;
|
||||||
|
return this.cache[model];
|
||||||
|
};
|
||||||
|
|
||||||
|
Memory.prototype.collectionSeq = function(model, val) {
|
||||||
|
model = this.getCollection(model);
|
||||||
|
if (arguments.length > 1) this.ids[model] = val;
|
||||||
|
return this.ids[model];
|
||||||
|
};
|
||||||
|
|
||||||
Memory.prototype.loadFromFile = function(callback) {
|
Memory.prototype.loadFromFile = function(callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var hasLocalStorage = typeof window !== 'undefined' && window.localStorage;
|
var hasLocalStorage = typeof window !== 'undefined' && window.localStorage;
|
||||||
|
@ -161,36 +186,31 @@ Memory.prototype.saveToFile = function (result, callback) {
|
||||||
Memory.prototype.define = function defineModel(definition) {
|
Memory.prototype.define = function defineModel(definition) {
|
||||||
this.constructor.super_.prototype.define.apply(this, [].slice.call(arguments));
|
this.constructor.super_.prototype.define.apply(this, [].slice.call(arguments));
|
||||||
var m = definition.model.modelName;
|
var m = definition.model.modelName;
|
||||||
if(!this.cache[m]) {
|
if(!this.collection(m)) this.initCollection(m);
|
||||||
this.cache[m] = {};
|
|
||||||
this.ids[m] = 1;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.create = function create(model, data, callback) {
|
Memory.prototype.create = function create(model, data, callback) {
|
||||||
// FIXME: [rfeng] We need to generate unique ids based on the id type
|
// FIXME: [rfeng] We need to generate unique ids based on the id type
|
||||||
// FIXME: [rfeng] We don't support composite ids yet
|
// FIXME: [rfeng] We don't support composite ids yet
|
||||||
var currentId = this.ids[model];
|
var currentId = this.collectionSeq(model);
|
||||||
if (currentId === undefined) {
|
if (currentId === undefined) { // First time
|
||||||
// First time
|
currentId = this.collectionSeq(model, 1);
|
||||||
this.ids[model] = 1;
|
|
||||||
currentId = 1;
|
|
||||||
}
|
}
|
||||||
var id = this.getIdValue(model, data) || currentId;
|
var id = this.getIdValue(model, data) || currentId;
|
||||||
if (id > currentId) {
|
if (id > currentId) {
|
||||||
// If the id is passed in and the value is greater than the current id
|
// If the id is passed in and the value is greater than the current id
|
||||||
currentId = id;
|
currentId = id;
|
||||||
}
|
}
|
||||||
this.ids[model] = Number(currentId) + 1;
|
this.collectionSeq(model, Number(currentId) + 1);
|
||||||
|
|
||||||
var props = this._models[model].properties;
|
var props = this._models[model].properties;
|
||||||
var idName = this.idName(model);
|
var idName = this.idName(model);
|
||||||
id = (props[idName] && props[idName].type && props[idName].type(id)) || id;
|
id = (props[idName] && props[idName].type && props[idName].type(id)) || id;
|
||||||
this.setIdValue(model, data, id);
|
this.setIdValue(model, data, id);
|
||||||
if(!this.cache[model]) {
|
if(!this.collection(model)) {
|
||||||
this.cache[model] = {};
|
this.collection(model, {});
|
||||||
}
|
}
|
||||||
this.cache[model][id] = serialize(data);
|
this.collection(model)[id] = serialize(data);
|
||||||
this.saveToFile(id, callback);
|
this.saveToFile(id, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -210,30 +230,30 @@ Memory.prototype.updateOrCreate = function (model, data, callback) {
|
||||||
|
|
||||||
Memory.prototype.save = function save(model, data, callback) {
|
Memory.prototype.save = function save(model, data, callback) {
|
||||||
var id = this.getIdValue(model, data);
|
var id = this.getIdValue(model, data);
|
||||||
var cachedModels = this.cache[model];
|
var cachedModels = this.collection(model);
|
||||||
var modelData = cachedModels && this.cache[model][id];
|
var modelData = cachedModels && this.collection(model)[id];
|
||||||
modelData = modelData && deserialize(modelData);
|
modelData = modelData && deserialize(modelData);
|
||||||
if (modelData) {
|
if (modelData) {
|
||||||
data = merge(modelData, data);
|
data = merge(modelData, data);
|
||||||
}
|
}
|
||||||
this.cache[model][id] = serialize(data);
|
this.collection(model)[id] = serialize(data);
|
||||||
this.saveToFile(data, callback);
|
this.saveToFile(data, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.exists = function exists(model, id, callback) {
|
Memory.prototype.exists = function exists(model, id, callback) {
|
||||||
process.nextTick(function () {
|
process.nextTick(function () {
|
||||||
callback(null, this.cache[model] && this.cache[model].hasOwnProperty(id));
|
callback(null, this.collection(model) && this.collection(model).hasOwnProperty(id));
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.find = function find(model, id, callback) {
|
Memory.prototype.find = function find(model, id, callback) {
|
||||||
process.nextTick(function () {
|
process.nextTick(function () {
|
||||||
callback(null, id in this.cache[model] && this.fromDb(model, this.cache[model][id]));
|
callback(null, id in this.collection(model) && this.fromDb(model, this.collection(model)[id]));
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.destroy = function destroy(model, id, callback) {
|
Memory.prototype.destroy = function destroy(model, id, callback) {
|
||||||
delete this.cache[model][id];
|
delete this.collection(model)[id];
|
||||||
this.saveToFile(null, callback);
|
this.saveToFile(null, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -266,8 +286,8 @@ Memory.prototype.fromDb = function (model, data) {
|
||||||
|
|
||||||
Memory.prototype.all = function all(model, filter, callback) {
|
Memory.prototype.all = function all(model, filter, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var nodes = Object.keys(this.cache[model]).map(function (key) {
|
var nodes = Object.keys(this.collection(model)).map(function (key) {
|
||||||
return this.fromDb(model, this.cache[model][key]);
|
return this.fromDb(model, this.collection(model)[key]);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
if (filter) {
|
if (filter) {
|
||||||
|
@ -505,24 +525,23 @@ Memory.prototype.destroyAll = function destroyAll(model, where, callback) {
|
||||||
callback = where;
|
callback = where;
|
||||||
where = undefined;
|
where = undefined;
|
||||||
}
|
}
|
||||||
var cache = this.cache[model];
|
var cache = this.collection(model);
|
||||||
var filter = null;
|
var filter = null;
|
||||||
if (where) {
|
if (where) {
|
||||||
filter = applyFilter({where: where});
|
filter = applyFilter({where: where});
|
||||||
}
|
|
||||||
Object.keys(cache).forEach(function (id) {
|
Object.keys(cache).forEach(function (id) {
|
||||||
if (!filter || filter(this.fromDb(model, cache[id]))) {
|
if (!filter || filter(this.fromDb(model, cache[id]))) {
|
||||||
delete cache[id];
|
delete cache[id];
|
||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
if (!where) {
|
} else {
|
||||||
this.cache[model] = {};
|
this.collection(model, {});
|
||||||
}
|
}
|
||||||
this.saveToFile(null, callback);
|
this.saveToFile(null, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.count = function count(model, callback, where) {
|
Memory.prototype.count = function count(model, callback, where) {
|
||||||
var cache = this.cache[model];
|
var cache = this.collection(model);
|
||||||
var data = Object.keys(cache);
|
var data = Object.keys(cache);
|
||||||
if (where) {
|
if (where) {
|
||||||
var filter = {where: where};
|
var filter = {where: where};
|
||||||
|
@ -539,7 +558,7 @@ Memory.prototype.count = function count(model, callback, where) {
|
||||||
Memory.prototype.update =
|
Memory.prototype.update =
|
||||||
Memory.prototype.updateAll = function updateAll(model, where, data, cb) {
|
Memory.prototype.updateAll = function updateAll(model, where, data, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var cache = this.cache[model];
|
var cache = this.collection(model);
|
||||||
var filter = null;
|
var filter = null;
|
||||||
where = where || {};
|
where = where || {};
|
||||||
filter = applyFilter({where: where});
|
filter = applyFilter({where: where});
|
||||||
|
@ -571,8 +590,8 @@ Memory.prototype.updateAttributes = function updateAttributes(model, id, data, c
|
||||||
|
|
||||||
this.setIdValue(model, data, id);
|
this.setIdValue(model, data, id);
|
||||||
|
|
||||||
var cachedModels = this.cache[model];
|
var cachedModels = this.collection(model);
|
||||||
var modelData = cachedModels && this.cache[model][id];
|
var modelData = cachedModels && this.collection(model)[id];
|
||||||
|
|
||||||
if (modelData) {
|
if (modelData) {
|
||||||
this.save(model, data, cb);
|
this.save(model, data, cb);
|
||||||
|
@ -594,6 +613,16 @@ Memory.prototype.buildNearFilter = function (filter) {
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Memory.prototype.automigrate = function (models, cb) {
|
||||||
|
if (typeof models === 'function') cb = models, models = [];
|
||||||
|
if (models.length === 0) models = Object.keys(this._models);
|
||||||
|
var self = this;
|
||||||
|
models.forEach(function(m) {
|
||||||
|
self.initCollection(m);
|
||||||
|
});
|
||||||
|
if (cb) cb();
|
||||||
|
}
|
||||||
|
|
||||||
function merge(base, update) {
|
function merge(base, update) {
|
||||||
if (!base) {
|
if (!base) {
|
||||||
return update;
|
return update;
|
||||||
|
|
126
lib/dao.js
126
lib/dao.js
|
@ -13,11 +13,12 @@ var Relation = require('./relations.js');
|
||||||
var Inclusion = require('./include.js');
|
var Inclusion = require('./include.js');
|
||||||
var List = require('./list.js');
|
var List = require('./list.js');
|
||||||
var geo = require('./geo');
|
var geo = require('./geo');
|
||||||
var mergeQuery = require('./scope.js').mergeQuery;
|
|
||||||
var Memory = require('./connectors/memory').Memory;
|
var Memory = require('./connectors/memory').Memory;
|
||||||
var utils = require('./utils');
|
var utils = require('./utils');
|
||||||
var fieldsToArray = utils.fieldsToArray;
|
var fieldsToArray = utils.fieldsToArray;
|
||||||
var removeUndefined = utils.removeUndefined;
|
var removeUndefined = utils.removeUndefined;
|
||||||
|
var setScopeValuesFromWhere = utils.setScopeValuesFromWhere;
|
||||||
|
var mergeQuery = utils.mergeQuery;
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
|
|
||||||
|
@ -53,6 +54,14 @@ function setIdValue(m, data, value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function byIdQuery(m, id) {
|
||||||
|
var pk = idName(m);
|
||||||
|
var query = { where: {} };
|
||||||
|
query.where[pk] = id;
|
||||||
|
m.applyScope(query);
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
DataAccessObject._forDB = function (data) {
|
DataAccessObject._forDB = function (data) {
|
||||||
if (!(this.getDataSource().isRelational && this.getDataSource().isRelational())) {
|
if (!(this.getDataSource().isRelational && this.getDataSource().isRelational())) {
|
||||||
return data;
|
return data;
|
||||||
|
@ -69,6 +78,40 @@ DataAccessObject._forDB = function (data) {
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DataAccessObject.defaultScope = function(target, inst) {
|
||||||
|
var scope = this.definition.settings.scope;
|
||||||
|
if (typeof scope === 'function') {
|
||||||
|
scope = this.definition.settings.scope.call(this, target, inst);
|
||||||
|
}
|
||||||
|
return scope;
|
||||||
|
};
|
||||||
|
|
||||||
|
DataAccessObject.applyScope = function(query, inst) {
|
||||||
|
var scope = this.defaultScope(query, inst) || {};
|
||||||
|
if (typeof scope === 'object') {
|
||||||
|
mergeQuery(query, scope || {}, this.definition.settings.scoping);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
DataAccessObject.applyProperties = function(data, inst) {
|
||||||
|
var properties = this.definition.settings.properties;
|
||||||
|
properties = properties || this.definition.settings.attributes;
|
||||||
|
if (typeof properties === 'object') {
|
||||||
|
util._extend(data, properties);
|
||||||
|
} else if (typeof properties === 'function') {
|
||||||
|
util._extend(data, properties.call(this, data, inst) || {});
|
||||||
|
} else if (properties !== false) {
|
||||||
|
var scope = this.defaultScope(data, inst) || {};
|
||||||
|
if (typeof scope.where === 'object') {
|
||||||
|
setScopeValuesFromWhere(data, scope.where, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
DataAccessObject.lookupModel = function(data) {
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an instance of Model with given data and save to the attached data source. Callback is optional.
|
* Create an instance of Model with given data and save to the attached data source. Callback is optional.
|
||||||
* Example:
|
* Example:
|
||||||
|
@ -89,7 +132,7 @@ DataAccessObject.create = function (data, callback) {
|
||||||
if (stillConnecting(this.getDataSource(), this, arguments)) return;
|
if (stillConnecting(this.getDataSource(), this, arguments)) return;
|
||||||
|
|
||||||
var Model = this;
|
var Model = this;
|
||||||
var modelName = Model.modelName;
|
var self = this;
|
||||||
|
|
||||||
if (typeof data === 'function') {
|
if (typeof data === 'function') {
|
||||||
callback = data;
|
callback = data;
|
||||||
|
@ -116,6 +159,7 @@ DataAccessObject.create = function (data, callback) {
|
||||||
|
|
||||||
for (var i = 0; i < data.length; i += 1) {
|
for (var i = 0; i < data.length; i += 1) {
|
||||||
(function (d, i) {
|
(function (d, i) {
|
||||||
|
Model = self.lookupModel(d); // data-specific
|
||||||
instances.push(Model.create(d, function (err, inst) {
|
instances.push(Model.create(d, function (err, inst) {
|
||||||
if (err) {
|
if (err) {
|
||||||
errors[i] = err;
|
errors[i] = err;
|
||||||
|
@ -131,11 +175,16 @@ DataAccessObject.create = function (data, callback) {
|
||||||
function modelCreated() {
|
function modelCreated() {
|
||||||
if (--wait === 0) {
|
if (--wait === 0) {
|
||||||
callback(gotError ? errors : null, instances);
|
callback(gotError ? errors : null, instances);
|
||||||
if(!gotError) instances.forEach(Model.emit.bind('changed'));
|
if(!gotError) {
|
||||||
|
instances.forEach(function(inst) {
|
||||||
|
inst.constructor.emit('changed');
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var enforced = {};
|
||||||
var obj;
|
var obj;
|
||||||
var idValue = getIdValue(this, data);
|
var idValue = getIdValue(this, data);
|
||||||
|
|
||||||
|
@ -145,6 +194,13 @@ DataAccessObject.create = function (data, callback) {
|
||||||
} else {
|
} else {
|
||||||
obj = new Model(data);
|
obj = new Model(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.applyProperties(enforced, obj);
|
||||||
|
obj.setAttributes(enforced);
|
||||||
|
|
||||||
|
Model = this.lookupModel(data); // data-specific
|
||||||
|
if (Model !== obj.constructor) obj = new Model(data);
|
||||||
|
|
||||||
data = obj.toObject(true);
|
data = obj.toObject(true);
|
||||||
|
|
||||||
// validation required
|
// validation required
|
||||||
|
@ -161,6 +217,7 @@ DataAccessObject.create = function (data, callback) {
|
||||||
obj.trigger('save', function (saveDone) {
|
obj.trigger('save', function (saveDone) {
|
||||||
|
|
||||||
var _idName = idName(Model);
|
var _idName = idName(Model);
|
||||||
|
var modelName = Model.modelName;
|
||||||
this._adapter().create(modelName, this.constructor._forDB(obj.toObject(true)), function (err, id, rev) {
|
this._adapter().create(modelName, this.constructor._forDB(obj.toObject(true)), function (err, id, rev) {
|
||||||
if (id) {
|
if (id) {
|
||||||
obj.__data[_idName] = id;
|
obj.__data[_idName] = id;
|
||||||
|
@ -208,7 +265,7 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data
|
||||||
if (stillConnecting(this.getDataSource(), this, arguments)) {
|
if (stillConnecting(this.getDataSource(), this, arguments)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
var self = this;
|
||||||
var Model = this;
|
var Model = this;
|
||||||
if (!getIdValue(this, data)) {
|
if (!getIdValue(this, data)) {
|
||||||
return this.create(data, callback);
|
return this.create(data, callback);
|
||||||
|
@ -220,7 +277,9 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data
|
||||||
inst = new Model(data);
|
inst = new Model(data);
|
||||||
}
|
}
|
||||||
update = inst.toObject(false);
|
update = inst.toObject(false);
|
||||||
|
this.applyProperties(update, inst);
|
||||||
update = removeUndefined(update);
|
update = removeUndefined(update);
|
||||||
|
Model = this.lookupModel(update);
|
||||||
this.getDataSource().connector.updateOrCreate(Model.modelName, update, function (err, data) {
|
this.getDataSource().connector.updateOrCreate(Model.modelName, update, function (err, data) {
|
||||||
var obj;
|
var obj;
|
||||||
if (data && !(data instanceof Model)) {
|
if (data && !(data instanceof Model)) {
|
||||||
|
@ -242,6 +301,7 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data
|
||||||
if (inst) {
|
if (inst) {
|
||||||
inst.updateAttributes(data, callback);
|
inst.updateAttributes(data, callback);
|
||||||
} else {
|
} else {
|
||||||
|
Model = self.lookupModel(data);
|
||||||
var obj = new Model(data);
|
var obj = new Model(data);
|
||||||
obj.save(data, callback);
|
obj.save(data, callback);
|
||||||
}
|
}
|
||||||
|
@ -290,7 +350,9 @@ DataAccessObject.exists = function exists(id, cb) {
|
||||||
if (stillConnecting(this.getDataSource(), this, arguments)) return;
|
if (stillConnecting(this.getDataSource(), this, arguments)) return;
|
||||||
|
|
||||||
if (id !== undefined && id !== null && id !== '') {
|
if (id !== undefined && id !== null && id !== '') {
|
||||||
this.dataSource.connector.exists(this.modelName, id, cb);
|
this.count(byIdQuery(this, id).where, function(err, count) {
|
||||||
|
cb(err, err ? false : count === 1);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
cb(new Error('Model::exists requires the id argument'));
|
cb(new Error('Model::exists requires the id argument'));
|
||||||
}
|
}
|
||||||
|
@ -311,17 +373,7 @@ DataAccessObject.exists = function exists(id, cb) {
|
||||||
*/
|
*/
|
||||||
DataAccessObject.findById = function find(id, cb) {
|
DataAccessObject.findById = function find(id, cb) {
|
||||||
if (stillConnecting(this.getDataSource(), this, arguments)) return;
|
if (stillConnecting(this.getDataSource(), this, arguments)) return;
|
||||||
|
this.findOne(byIdQuery(this, id), cb);
|
||||||
this.getDataSource().connector.find(this.modelName, id, function (err, data) {
|
|
||||||
var obj = null;
|
|
||||||
if (data) {
|
|
||||||
if (!getIdValue(this, data)) {
|
|
||||||
setIdValue(this, data, id);
|
|
||||||
}
|
|
||||||
obj = new this(data, {applySetters: false, persisted: true});
|
|
||||||
}
|
|
||||||
cb(err, obj);
|
|
||||||
}.bind(this));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DataAccessObject.findByIds = function(ids, cond, cb) {
|
DataAccessObject.findByIds = function(ids, cond, cb) {
|
||||||
|
@ -692,6 +744,8 @@ DataAccessObject.find = function find(query, cb) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.applyScope(query);
|
||||||
|
|
||||||
var near = query && geo.nearFilter(query.where);
|
var near = query && geo.nearFilter(query.where);
|
||||||
var supportsGeo = !!this.getDataSource().connector.buildNearFilter;
|
var supportsGeo = !!this.getDataSource().connector.buildNearFilter;
|
||||||
|
|
||||||
|
@ -702,6 +756,7 @@ DataAccessObject.find = function find(query, cb) {
|
||||||
} else if (query.where) {
|
} else if (query.where) {
|
||||||
// do in memory query
|
// do in memory query
|
||||||
// using all documents
|
// using all documents
|
||||||
|
// TODO [fabien] use default scope here?
|
||||||
this.getDataSource().connector.all(this.modelName, {}, function (err, data) {
|
this.getDataSource().connector.all(this.modelName, {}, function (err, data) {
|
||||||
var memory = new Memory();
|
var memory = new Memory();
|
||||||
var modelName = self.modelName;
|
var modelName = self.modelName;
|
||||||
|
@ -735,7 +790,8 @@ DataAccessObject.find = function find(query, cb) {
|
||||||
this.getDataSource().connector.all(this.modelName, query, function (err, data) {
|
this.getDataSource().connector.all(this.modelName, query, function (err, data) {
|
||||||
if (data && data.forEach) {
|
if (data && data.forEach) {
|
||||||
data.forEach(function (d, i) {
|
data.forEach(function (d, i) {
|
||||||
var obj = new self(d, {fields: query.fields, applySetters: false, persisted: true});
|
var Model = self.lookupModel(d);
|
||||||
|
var obj = new Model(d, {fields: query.fields, applySetters: false, persisted: true});
|
||||||
|
|
||||||
if (query && query.include) {
|
if (query && query.include) {
|
||||||
if (query.collect) {
|
if (query.collect) {
|
||||||
|
@ -824,7 +880,12 @@ DataAccessObject.remove = DataAccessObject.deleteAll = DataAccessObject.destroyA
|
||||||
cb = where;
|
cb = where;
|
||||||
where = undefined;
|
where = undefined;
|
||||||
}
|
}
|
||||||
if (!where) {
|
|
||||||
|
var query = { where: where };
|
||||||
|
this.applyScope(query);
|
||||||
|
where = query.where;
|
||||||
|
|
||||||
|
if (!where || (typeof where === 'object' && Object.keys(where).length === 0)) {
|
||||||
this.getDataSource().connector.destroyAll(this.modelName, function (err, data) {
|
this.getDataSource().connector.destroyAll(this.modelName, function (err, data) {
|
||||||
cb && cb(err, data);
|
cb && cb(err, data);
|
||||||
if(!err) Model.emit('deletedAll');
|
if(!err) Model.emit('deletedAll');
|
||||||
|
@ -844,7 +905,7 @@ DataAccessObject.remove = DataAccessObject.deleteAll = DataAccessObject.destroyA
|
||||||
if(!err) Model.emit('deletedAll', where);
|
if(!err) Model.emit('deletedAll', where);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the record with the specified ID.
|
* Delete the record with the specified ID.
|
||||||
|
@ -860,13 +921,13 @@ DataAccessObject.removeById = DataAccessObject.destroyById = DataAccessObject.de
|
||||||
if (stillConnecting(this.getDataSource(), this, arguments)) return;
|
if (stillConnecting(this.getDataSource(), this, arguments)) return;
|
||||||
var Model = this;
|
var Model = this;
|
||||||
|
|
||||||
this.getDataSource().connector.destroy(this.modelName, id, function (err) {
|
this.remove(byIdQuery(this, id).where, function(err) {
|
||||||
if ('function' === typeof cb) {
|
if ('function' === typeof cb) {
|
||||||
cb(err);
|
cb(err);
|
||||||
}
|
}
|
||||||
if(!err) Model.emit('deleted', id);
|
if(!err) Model.emit('deleted', id);
|
||||||
}.bind(this));
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return count of matched records. Optional query parameter allows you to count filtered set of model instances.
|
* Return count of matched records. Optional query parameter allows you to count filtered set of model instances.
|
||||||
|
@ -888,6 +949,11 @@ DataAccessObject.count = function (where, cb) {
|
||||||
cb = where;
|
cb = where;
|
||||||
where = null;
|
where = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var query = { where: where };
|
||||||
|
this.applyScope(query);
|
||||||
|
where = query.where;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
where = removeUndefined(where);
|
where = removeUndefined(where);
|
||||||
where = this._coerce(where);
|
where = this._coerce(where);
|
||||||
|
@ -896,6 +962,7 @@ DataAccessObject.count = function (where, cb) {
|
||||||
cb && cb(err);
|
cb && cb(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getDataSource().connector.count(this.modelName, cb, where);
|
this.getDataSource().connector.count(this.modelName, cb, where);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -931,8 +998,12 @@ DataAccessObject.prototype.save = function (options, callback) {
|
||||||
var data = inst.toObject(true);
|
var data = inst.toObject(true);
|
||||||
var modelName = Model.modelName;
|
var modelName = Model.modelName;
|
||||||
|
|
||||||
|
Model.applyProperties(data, this);
|
||||||
|
|
||||||
if (this.isNewRecord()) {
|
if (this.isNewRecord()) {
|
||||||
return Model.create(this, callback);
|
return Model.create(this, callback);
|
||||||
|
} else {
|
||||||
|
inst.setAttributes(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate first
|
// validate first
|
||||||
|
@ -1017,6 +1088,12 @@ DataAccessObject.updateAll = function (where, data, cb) {
|
||||||
assert(typeof data === 'object', 'The data argument should be an object');
|
assert(typeof data === 'object', 'The data argument should be an object');
|
||||||
assert(cb === null || typeof cb === 'function', 'The cb argument should be a function');
|
assert(cb === null || typeof cb === 'function', 'The cb argument should be a function');
|
||||||
|
|
||||||
|
var query = { where: where };
|
||||||
|
this.applyScope(query);
|
||||||
|
this.applyProperties(data);
|
||||||
|
|
||||||
|
where = query.where;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
where = removeUndefined(where);
|
where = removeUndefined(where);
|
||||||
where = this._coerce(where);
|
where = this._coerce(where);
|
||||||
|
@ -1025,6 +1102,7 @@ DataAccessObject.updateAll = function (where, data, cb) {
|
||||||
cb && cb(err);
|
cb && cb(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var connector = this.getDataSource().connector;
|
var connector = this.getDataSource().connector;
|
||||||
connector.update(this.modelName, where, data, cb);
|
connector.update(this.modelName, where, data, cb);
|
||||||
};
|
};
|
||||||
|
@ -1075,7 +1153,7 @@ DataAccessObject.prototype.remove =
|
||||||
* @param {Mixed} value Value of property
|
* @param {Mixed} value Value of property
|
||||||
*/
|
*/
|
||||||
DataAccessObject.prototype.setAttribute = function setAttribute(name, value) {
|
DataAccessObject.prototype.setAttribute = function setAttribute(name, value) {
|
||||||
this[name] = value;
|
this[name] = value; // TODO [fabien] - currently not protected by applyProperties
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1101,6 +1179,8 @@ DataAccessObject.prototype.updateAttribute = function updateAttribute(name, valu
|
||||||
DataAccessObject.prototype.setAttributes = function setAttributes(data) {
|
DataAccessObject.prototype.setAttributes = function setAttributes(data) {
|
||||||
if (typeof data !== 'object') return;
|
if (typeof data !== 'object') return;
|
||||||
|
|
||||||
|
this.constructor.applyProperties(data, this);
|
||||||
|
|
||||||
var Model = this.constructor;
|
var Model = this.constructor;
|
||||||
var inst = this;
|
var inst = this;
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,10 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
|
||||||
var properties = _extend({}, ctor.definition.properties);
|
var properties = _extend({}, ctor.definition.properties);
|
||||||
data = data || {};
|
data = data || {};
|
||||||
|
|
||||||
|
if (typeof ctor.applyProperties === 'function') {
|
||||||
|
ctor.applyProperties(data);
|
||||||
|
}
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
var applySetters = options.applySetters;
|
var applySetters = options.applySetters;
|
||||||
var strict = options.strict;
|
var strict = options.strict;
|
||||||
|
@ -130,7 +134,7 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
|
||||||
}
|
}
|
||||||
if (properties[p]) {
|
if (properties[p]) {
|
||||||
// Managed property
|
// Managed property
|
||||||
if (applySetters) {
|
if (applySetters || properties[p].id) {
|
||||||
self[p] = propVal;
|
self[p] = propVal;
|
||||||
} else {
|
} else {
|
||||||
self.__data[p] = propVal;
|
self.__data[p] = propVal;
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
*/
|
*/
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
var utils = require('./utils');
|
||||||
var i8n = require('inflection');
|
var i8n = require('inflection');
|
||||||
var defineScope = require('./scope.js').defineScope;
|
var defineScope = require('./scope.js').defineScope;
|
||||||
var mergeQuery = require('./scope.js').mergeQuery;
|
var mergeQuery = utils.mergeQuery;
|
||||||
var ModelBaseClass = require('./model.js');
|
var ModelBaseClass = require('./model.js');
|
||||||
var applyFilter = require('./connectors/memory').applyFilter;
|
var applyFilter = require('./connectors/memory').applyFilter;
|
||||||
var ValidationError = require('./validations.js').ValidationError;
|
var ValidationError = require('./validations.js').ValidationError;
|
||||||
|
|
89
lib/scope.js
89
lib/scope.js
|
@ -1,13 +1,14 @@
|
||||||
var i8n = require('inflection');
|
var i8n = require('inflection');
|
||||||
var utils = require('./utils');
|
var utils = require('./utils');
|
||||||
var defineCachedRelations = utils.defineCachedRelations;
|
var defineCachedRelations = utils.defineCachedRelations;
|
||||||
|
var setScopeValuesFromWhere = utils.setScopeValuesFromWhere;
|
||||||
|
var mergeQuery = utils.mergeQuery;
|
||||||
var DefaultModelBaseClass = require('./model.js');
|
var DefaultModelBaseClass = require('./model.js');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module exports
|
* Module exports
|
||||||
*/
|
*/
|
||||||
exports.defineScope = defineScope;
|
exports.defineScope = defineScope;
|
||||||
exports.mergeQuery = mergeQuery;
|
|
||||||
|
|
||||||
function ScopeDefinition(definition) {
|
function ScopeDefinition(definition) {
|
||||||
this.isStatic = definition.isStatic;
|
this.isStatic = definition.isStatic;
|
||||||
|
@ -229,35 +230,6 @@ function defineScope(cls, targetClass, name, params, methods, options) {
|
||||||
|
|
||||||
cls['__count__' + name] = fn_count;
|
cls['__count__' + name] = fn_count;
|
||||||
|
|
||||||
/*
|
|
||||||
* Extracting fixed property values for the scope from the where clause into
|
|
||||||
* the data object
|
|
||||||
*
|
|
||||||
* @param {Object} The data object
|
|
||||||
* @param {Object} The where clause
|
|
||||||
*/
|
|
||||||
function setScopeValuesFromWhere(data, where, targetModel) {
|
|
||||||
for (var i in where) {
|
|
||||||
if (i === 'and') {
|
|
||||||
// Find fixed property values from each subclauses
|
|
||||||
for (var w = 0, n = where[i].length; w < n; w++) {
|
|
||||||
setScopeValuesFromWhere(data, where[i][w], targetModel);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var prop = targetModel.definition.properties[i];
|
|
||||||
if (prop) {
|
|
||||||
var val = where[i];
|
|
||||||
if (typeof val !== 'object' || val instanceof prop.type
|
|
||||||
|| prop.type.name === 'ObjectID') // MongoDB key
|
|
||||||
{
|
|
||||||
// Only pick the {propertyName: propertyValue}
|
|
||||||
data[i] = where[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// and it should have create/build methods with binded thisModelNameId param
|
// and it should have create/build methods with binded thisModelNameId param
|
||||||
function build(data) {
|
function build(data) {
|
||||||
data = data || {};
|
data = data || {};
|
||||||
|
@ -300,60 +272,3 @@ function defineScope(cls, targetClass, name, params, methods, options) {
|
||||||
|
|
||||||
return definition;
|
return definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* Merge query parameters
|
|
||||||
* @param {Object} base The base object to contain the merged results
|
|
||||||
* @param {Object} update The object containing updates to be merged
|
|
||||||
* @returns {*|Object} The base object
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
function mergeQuery(base, update) {
|
|
||||||
if (!update) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
base = base || {};
|
|
||||||
if (update.where && Object.keys(update.where).length > 0) {
|
|
||||||
if (base.where && Object.keys(base.where).length > 0) {
|
|
||||||
base.where = {and: [base.where, update.where]};
|
|
||||||
} else {
|
|
||||||
base.where = update.where;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge inclusion
|
|
||||||
if (update.include) {
|
|
||||||
if (!base.include) {
|
|
||||||
base.include = update.include;
|
|
||||||
} else {
|
|
||||||
var saved = base.include;
|
|
||||||
base.include = {};
|
|
||||||
base.include[update.include] = saved;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (update.collect) {
|
|
||||||
base.collect = update.collect;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set order
|
|
||||||
if (!base.order && update.order) {
|
|
||||||
base.order = update.order;
|
|
||||||
}
|
|
||||||
|
|
||||||
// overwrite pagination
|
|
||||||
if (update.limit !== undefined) {
|
|
||||||
base.limit = update.limit;
|
|
||||||
}
|
|
||||||
if (update.skip !== undefined) {
|
|
||||||
base.skip = update.skip;
|
|
||||||
}
|
|
||||||
if (update.offset !== undefined) {
|
|
||||||
base.offset = update.offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overwrite fields
|
|
||||||
if (update.fields !== undefined) {
|
|
||||||
base.fields = update.fields;
|
|
||||||
}
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
|
|
97
lib/utils.js
97
lib/utils.js
|
@ -7,6 +7,8 @@ exports.mergeSettings = mergeSettings;
|
||||||
exports.isPlainObject = isPlainObject;
|
exports.isPlainObject = isPlainObject;
|
||||||
exports.defineCachedRelations = defineCachedRelations;
|
exports.defineCachedRelations = defineCachedRelations;
|
||||||
exports.sortObjectsByIds = sortObjectsByIds;
|
exports.sortObjectsByIds = sortObjectsByIds;
|
||||||
|
exports.setScopeValuesFromWhere = setScopeValuesFromWhere;
|
||||||
|
exports.mergeQuery = mergeQuery;
|
||||||
|
|
||||||
var traverse = require('traverse');
|
var traverse = require('traverse');
|
||||||
|
|
||||||
|
@ -21,6 +23,101 @@ function safeRequire(module) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extracting fixed property values for the scope from the where clause into
|
||||||
|
* the data object
|
||||||
|
*
|
||||||
|
* @param {Object} The data object
|
||||||
|
* @param {Object} The where clause
|
||||||
|
*/
|
||||||
|
function setScopeValuesFromWhere(data, where, targetModel) {
|
||||||
|
for (var i in where) {
|
||||||
|
if (i === 'and') {
|
||||||
|
// Find fixed property values from each subclauses
|
||||||
|
for (var w = 0, n = where[i].length; w < n; w++) {
|
||||||
|
setScopeValuesFromWhere(data, where[i][w], targetModel);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var prop = targetModel.definition.properties[i];
|
||||||
|
if (prop) {
|
||||||
|
var val = where[i];
|
||||||
|
if (typeof val !== 'object' || val instanceof prop.type
|
||||||
|
|| prop.type.name === 'ObjectID') // MongoDB key
|
||||||
|
{
|
||||||
|
// Only pick the {propertyName: propertyValue}
|
||||||
|
data[i] = where[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Merge query parameters
|
||||||
|
* @param {Object} base The base object to contain the merged results
|
||||||
|
* @param {Object} update The object containing updates to be merged
|
||||||
|
* @param {Object} spec Optionally specifies parameters to exclude (set to false)
|
||||||
|
* @returns {*|Object} The base object
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function mergeQuery(base, update, spec) {
|
||||||
|
if (!update) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
spec = spec || {};
|
||||||
|
base = base || {};
|
||||||
|
|
||||||
|
if (update.where && Object.keys(update.where).length > 0) {
|
||||||
|
if (base.where && Object.keys(base.where).length > 0) {
|
||||||
|
base.where = {and: [base.where, update.where]};
|
||||||
|
} else {
|
||||||
|
base.where = update.where;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge inclusion
|
||||||
|
if (spec.include !== false && update.include) {
|
||||||
|
if (!base.include) {
|
||||||
|
base.include = update.include;
|
||||||
|
} else {
|
||||||
|
var saved = base.include;
|
||||||
|
base.include = {};
|
||||||
|
base.include[update.include] = saved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spec.collect !== false && update.collect) {
|
||||||
|
base.collect = update.collect;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overwrite fields
|
||||||
|
if (spec.fields !== false && update.fields !== undefined) {
|
||||||
|
base.fields = update.fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set order
|
||||||
|
if ((!base.order || spec.order === false) && update.order) {
|
||||||
|
base.order = update.order;
|
||||||
|
}
|
||||||
|
|
||||||
|
// overwrite pagination
|
||||||
|
if (spec.limit !== false && update.limit !== undefined) {
|
||||||
|
base.limit = update.limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
var skip = spec.skip !== false && spec.offset !== false;
|
||||||
|
|
||||||
|
if (skip && update.skip !== undefined) {
|
||||||
|
base.skip = update.skip;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skip && update.offset !== undefined) {
|
||||||
|
base.offset = update.offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
function fieldsToArray(fields, properties) {
|
function fieldsToArray(fields, properties) {
|
||||||
if (!fields) return;
|
if (!fields) return;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,808 @@
|
||||||
|
// This test written in mocha+should.js
|
||||||
|
var should = require('./init.js');
|
||||||
|
var async = require('async');
|
||||||
|
|
||||||
|
var db, Category, Product, Tool, Widget, Thing;
|
||||||
|
|
||||||
|
// This test requires a connector that can
|
||||||
|
// handle a custom collection or table name
|
||||||
|
|
||||||
|
// TODO [fabien] add table for pgsql/mysql
|
||||||
|
// TODO [fabien] change model definition - see #293
|
||||||
|
|
||||||
|
var setupProducts = function(ids, done) {
|
||||||
|
async.series([
|
||||||
|
function(next) {
|
||||||
|
Tool.create({name: 'Tool Z'}, function(err, inst) {
|
||||||
|
ids.toolZ = inst.id;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
Widget.create({name: 'Widget Z'}, function(err, inst) {
|
||||||
|
ids.widgetZ = inst.id;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
Tool.create({name: 'Tool A', active: false}, function(err, inst) {
|
||||||
|
ids.toolA = inst.id;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
Widget.create({name: 'Widget A'}, function(err, inst) {
|
||||||
|
ids.widgetA = inst.id;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
Widget.create({name: 'Widget B', active: false}, function(err, inst) {
|
||||||
|
ids.widgetB = inst.id;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
], done);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('default scope', function () {
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
db = getSchema();
|
||||||
|
|
||||||
|
Category = db.define('Category', {
|
||||||
|
name: String
|
||||||
|
});
|
||||||
|
|
||||||
|
Product = db.define('Product', {
|
||||||
|
name: String,
|
||||||
|
kind: String,
|
||||||
|
description: String,
|
||||||
|
active: { type: Boolean, default: true }
|
||||||
|
}, {
|
||||||
|
scope: { order: 'name' },
|
||||||
|
scopes: { active: { where: { active: true } } }
|
||||||
|
});
|
||||||
|
|
||||||
|
Product.lookupModel = function(data) {
|
||||||
|
var m = this.dataSource.models[data.kind];
|
||||||
|
if (m.base === this) return m;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Tool = db.define('Tool', Product.definition.properties, {
|
||||||
|
base: 'Product',
|
||||||
|
scope: { where: { kind: 'Tool' }, order: 'name' },
|
||||||
|
scopes: { active: { where: { active: true } } },
|
||||||
|
mongodb: { collection: 'Product' },
|
||||||
|
memory: { collection: 'Product' }
|
||||||
|
});
|
||||||
|
|
||||||
|
Widget = db.define('Widget', Product.definition.properties, {
|
||||||
|
base: 'Product',
|
||||||
|
properties: { kind: 'Widget' },
|
||||||
|
scope: { where: { kind: 'Widget' }, order: 'name' },
|
||||||
|
scopes: { active: { where: { active: true } } },
|
||||||
|
mongodb: { collection: 'Product' },
|
||||||
|
memory: { collection: 'Product' }
|
||||||
|
});
|
||||||
|
|
||||||
|
// inst is only valid for instance methods
|
||||||
|
// like save, updateAttributes
|
||||||
|
|
||||||
|
var scopeFn = function(target, inst) {
|
||||||
|
return { where: { kind: this.modelName } };
|
||||||
|
};
|
||||||
|
|
||||||
|
var propertiesFn = function(target, inst) {
|
||||||
|
return { kind: this.modelName };
|
||||||
|
};
|
||||||
|
|
||||||
|
Thing = db.define('Thing', Product.definition.properties, {
|
||||||
|
base: 'Product',
|
||||||
|
attributes: propertiesFn,
|
||||||
|
scope: scopeFn,
|
||||||
|
mongodb: { collection: 'Product' },
|
||||||
|
memory: { collection: 'Product' }
|
||||||
|
});
|
||||||
|
|
||||||
|
Category.hasMany(Product);
|
||||||
|
Category.hasMany(Tool, {scope: {order: 'name DESC'}});
|
||||||
|
Category.hasMany(Widget);
|
||||||
|
Category.hasMany(Thing);
|
||||||
|
|
||||||
|
Product.belongsTo(Category);
|
||||||
|
Tool.belongsTo(Category);
|
||||||
|
Widget.belongsTo(Category);
|
||||||
|
Thing.belongsTo(Category);
|
||||||
|
|
||||||
|
db.automigrate(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('manipulation', function() {
|
||||||
|
|
||||||
|
var ids = {};
|
||||||
|
|
||||||
|
before(function(done) {
|
||||||
|
db.automigrate(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a scoped instance', function() {
|
||||||
|
var p = new Tool({name: 'Product A', kind:'ignored'});
|
||||||
|
p.name.should.equal('Product A');
|
||||||
|
p.kind.should.equal('Tool');
|
||||||
|
p.setAttributes({ kind: 'ignored' });
|
||||||
|
p.kind.should.equal('Tool');
|
||||||
|
|
||||||
|
p.setAttribute('kind', 'other'); // currently not enforced
|
||||||
|
p.kind.should.equal('other');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a scoped instance - tool', function(done) {
|
||||||
|
Tool.create({name: 'Product A', kind: 'ignored'}, function(err, p) {
|
||||||
|
should.not.exist(err);
|
||||||
|
p.name.should.equal('Product A');
|
||||||
|
p.kind.should.equal('Tool');
|
||||||
|
ids.productA = p.id;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a scoped instance - widget', function(done) {
|
||||||
|
Widget.create({name: 'Product B', kind: 'ignored'}, function(err, p) {
|
||||||
|
should.not.exist(err);
|
||||||
|
p.name.should.equal('Product B');
|
||||||
|
p.kind.should.equal('Widget');
|
||||||
|
ids.productB = p.id;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update a scoped instance - updateAttributes', function(done) {
|
||||||
|
Tool.findById(ids.productA, function(err, p) {
|
||||||
|
p.updateAttributes({description: 'A thing...', kind: 'ingored'}, function(err, inst) {
|
||||||
|
should.not.exist(err);
|
||||||
|
p.name.should.equal('Product A');
|
||||||
|
p.kind.should.equal('Tool');
|
||||||
|
p.description.should.equal('A thing...');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update a scoped instance - save', function(done) {
|
||||||
|
Tool.findById(ids.productA, function(err, p) {
|
||||||
|
p.description = 'Something...';
|
||||||
|
p.kind = 'ignored';
|
||||||
|
p.save(function(err, inst) {
|
||||||
|
should.not.exist(err);
|
||||||
|
p.name.should.equal('Product A');
|
||||||
|
p.kind.should.equal('Tool');
|
||||||
|
p.description.should.equal('Something...');
|
||||||
|
Tool.findById(ids.productA, function(err, p) {
|
||||||
|
p.kind.should.equal('Tool');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update a scoped instance - updateOrCreate', function(done) {
|
||||||
|
var data = {id: ids.productA, description: 'Anything...', kind: 'ingored'};
|
||||||
|
Tool.updateOrCreate(data, function(err, p) {
|
||||||
|
should.not.exist(err);
|
||||||
|
p.name.should.equal('Product A');
|
||||||
|
p.kind.should.equal('Tool');
|
||||||
|
p.description.should.equal('Anything...');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('findById', function() {
|
||||||
|
|
||||||
|
var ids = {};
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
db.automigrate(setupProducts.bind(null, ids, done));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope', function(done) {
|
||||||
|
Product.findById(ids.toolA, function(err, inst) {
|
||||||
|
should.not.exist(err);
|
||||||
|
inst.name.should.equal('Tool A');
|
||||||
|
inst.should.be.instanceof(Tool);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - tool', function(done) {
|
||||||
|
Tool.findById(ids.toolA, function(err, inst) {
|
||||||
|
should.not.exist(err);
|
||||||
|
inst.name.should.equal('Tool A');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope (no match)', function(done) {
|
||||||
|
Widget.findById(ids.toolA, function(err, inst) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.not.exist(inst);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('find', function() {
|
||||||
|
|
||||||
|
var ids = {};
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
db.automigrate(setupProducts.bind(null, ids, done));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - order', function(done) {
|
||||||
|
Product.find(function(err, products) {
|
||||||
|
should.not.exist(err);
|
||||||
|
products.should.have.length(5);
|
||||||
|
products[0].name.should.equal('Tool A');
|
||||||
|
products[1].name.should.equal('Tool Z');
|
||||||
|
products[2].name.should.equal('Widget A');
|
||||||
|
products[3].name.should.equal('Widget B');
|
||||||
|
products[4].name.should.equal('Widget Z');
|
||||||
|
|
||||||
|
products[0].should.be.instanceof(Product);
|
||||||
|
products[0].should.be.instanceof(Tool);
|
||||||
|
|
||||||
|
products[2].should.be.instanceof(Product);
|
||||||
|
products[2].should.be.instanceof(Widget);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - order override', function(done) {
|
||||||
|
Product.find({ order: 'name DESC' }, function(err, products) {
|
||||||
|
should.not.exist(err);
|
||||||
|
products.should.have.length(5);
|
||||||
|
products[0].name.should.equal('Widget Z');
|
||||||
|
products[1].name.should.equal('Widget B');
|
||||||
|
products[2].name.should.equal('Widget A');
|
||||||
|
products[3].name.should.equal('Tool Z');
|
||||||
|
products[4].name.should.equal('Tool A');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - tool', function(done) {
|
||||||
|
Tool.find(function(err, products) {
|
||||||
|
should.not.exist(err);
|
||||||
|
products.should.have.length(2);
|
||||||
|
products[0].name.should.equal('Tool A');
|
||||||
|
products[1].name.should.equal('Tool Z');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - where (widget)', function(done) {
|
||||||
|
Widget.find({ where: { active: true } }, function(err, products) {
|
||||||
|
should.not.exist(err);
|
||||||
|
products.should.have.length(2);
|
||||||
|
products[0].name.should.equal('Widget A');
|
||||||
|
products[1].name.should.equal('Widget Z');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - order (widget)', function(done) {
|
||||||
|
Widget.find({ order: 'name DESC' }, function(err, products) {
|
||||||
|
should.not.exist(err);
|
||||||
|
products.should.have.length(3);
|
||||||
|
products[0].name.should.equal('Widget Z');
|
||||||
|
products[1].name.should.equal('Widget B');
|
||||||
|
products[2].name.should.equal('Widget A');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('exists', function() {
|
||||||
|
|
||||||
|
var ids = {};
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
db.automigrate(setupProducts.bind(null, ids, done));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope', function(done) {
|
||||||
|
Product.exists(ids.widgetA, function(err, exists) {
|
||||||
|
should.not.exist(err);
|
||||||
|
exists.should.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - tool', function(done) {
|
||||||
|
Tool.exists(ids.toolZ, function(err, exists) {
|
||||||
|
should.not.exist(err);
|
||||||
|
exists.should.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - widget', function(done) {
|
||||||
|
Widget.exists(ids.widgetA, function(err, exists) {
|
||||||
|
should.not.exist(err);
|
||||||
|
exists.should.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - tool (no match)', function(done) {
|
||||||
|
Tool.exists(ids.widgetA, function(err, exists) {
|
||||||
|
should.not.exist(err);
|
||||||
|
exists.should.be.false;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - widget (no match)', function(done) {
|
||||||
|
Widget.exists(ids.toolZ, function(err, exists) {
|
||||||
|
should.not.exist(err);
|
||||||
|
exists.should.be.false;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('count', function() {
|
||||||
|
|
||||||
|
var ids = {};
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
db.automigrate(setupProducts.bind(null, ids, done));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - order', function(done) {
|
||||||
|
Product.count(function(err, count) {
|
||||||
|
should.not.exist(err);
|
||||||
|
count.should.equal(5);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - tool', function(done) {
|
||||||
|
Tool.count(function(err, count) {
|
||||||
|
should.not.exist(err);
|
||||||
|
count.should.equal(2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - widget', function(done) {
|
||||||
|
Widget.count(function(err, count) {
|
||||||
|
should.not.exist(err);
|
||||||
|
count.should.equal(3);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - where', function(done) {
|
||||||
|
Widget.count({name: 'Widget Z'}, function(err, count) {
|
||||||
|
should.not.exist(err);
|
||||||
|
count.should.equal(1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - no match', function(done) {
|
||||||
|
Tool.count({name: 'Widget Z'}, function(err, count) {
|
||||||
|
should.not.exist(err);
|
||||||
|
count.should.equal(0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('removeById', function() {
|
||||||
|
|
||||||
|
var ids = {};
|
||||||
|
|
||||||
|
function isDeleted(id, done) {
|
||||||
|
Product.exists(id, function(err, exists) {
|
||||||
|
should.not.exist(err);
|
||||||
|
exists.should.be.false;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
db.automigrate(setupProducts.bind(null, ids, done));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope', function(done) {
|
||||||
|
Product.removeById(ids.widgetZ, function(err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
isDeleted(ids.widgetZ, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - tool', function(done) {
|
||||||
|
Tool.removeById(ids.toolA, function(err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
isDeleted(ids.toolA, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - no match', function(done) {
|
||||||
|
Tool.removeById(ids.widgetA, function(err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
Product.exists(ids.widgetA, function(err, exists) {
|
||||||
|
should.not.exist(err);
|
||||||
|
exists.should.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - widget', function(done) {
|
||||||
|
Widget.removeById(ids.widgetA, function(err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
isDeleted(ids.widgetA, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - verify', function(done) {
|
||||||
|
Product.find(function(err, products) {
|
||||||
|
should.not.exist(err);
|
||||||
|
products.should.have.length(2);
|
||||||
|
products[0].name.should.equal('Tool Z');
|
||||||
|
products[1].name.should.equal('Widget B');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('update', function() {
|
||||||
|
|
||||||
|
var ids = {};
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
db.automigrate(setupProducts.bind(null, ids, done));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope', function(done) {
|
||||||
|
Widget.update({active: false},{active: true, kind: 'ignored'}, function(err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
Widget.find({where: { active: true }}, function(err, products) {
|
||||||
|
should.not.exist(err);
|
||||||
|
products.should.have.length(3);
|
||||||
|
products[0].name.should.equal('Widget A');
|
||||||
|
products[1].name.should.equal('Widget B');
|
||||||
|
products[2].name.should.equal('Widget Z');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - no match', function(done) {
|
||||||
|
Tool.update({name: 'Widget A'},{name: 'Ignored'}, function(err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
Product.findById(ids.widgetA, function(err, product) {
|
||||||
|
should.not.exist(err);
|
||||||
|
product.name.should.equal('Widget A');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have updated within scope', function(done) {
|
||||||
|
Product.find({where: {active: true}}, function(err, products) {
|
||||||
|
should.not.exist(err);
|
||||||
|
products.should.have.length(4);
|
||||||
|
products[0].name.should.equal('Tool Z');
|
||||||
|
products[1].name.should.equal('Widget A');
|
||||||
|
products[2].name.should.equal('Widget B');
|
||||||
|
products[3].name.should.equal('Widget Z');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('remove', function() {
|
||||||
|
|
||||||
|
var ids = {};
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
db.automigrate(setupProducts.bind(null, ids, done));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - custom where', function(done) {
|
||||||
|
Widget.remove({name: 'Widget A'}, function(err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
Product.find(function(err, products) {
|
||||||
|
products.should.have.length(4);
|
||||||
|
products[0].name.should.equal('Tool A');
|
||||||
|
products[1].name.should.equal('Tool Z');
|
||||||
|
products[2].name.should.equal('Widget B');
|
||||||
|
products[3].name.should.equal('Widget Z');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - custom where (no match)', function(done) {
|
||||||
|
Tool.remove({name: 'Widget Z'}, function(err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
Product.find(function(err, products) {
|
||||||
|
products.should.have.length(4);
|
||||||
|
products[0].name.should.equal('Tool A');
|
||||||
|
products[1].name.should.equal('Tool Z');
|
||||||
|
products[2].name.should.equal('Widget B');
|
||||||
|
products[3].name.should.equal('Widget Z');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - deleteAll', function(done) {
|
||||||
|
Tool.deleteAll(function(err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
Product.find(function(err, products) {
|
||||||
|
products.should.have.length(2);
|
||||||
|
products[0].name.should.equal('Widget B');
|
||||||
|
products[1].name.should.equal('Widget Z');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a scoped instance - tool', function(done) {
|
||||||
|
Tool.create({name: 'Tool B'}, function(err, p) {
|
||||||
|
should.not.exist(err);
|
||||||
|
Product.find(function(err, products) {
|
||||||
|
products.should.have.length(3);
|
||||||
|
products[0].name.should.equal('Tool B');
|
||||||
|
products[1].name.should.equal('Widget B');
|
||||||
|
products[2].name.should.equal('Widget Z');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - destroyAll', function(done) {
|
||||||
|
Widget.destroyAll(function(err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
Product.find(function(err, products) {
|
||||||
|
products.should.have.length(1);
|
||||||
|
products[0].name.should.equal('Tool B');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('scopes', function() {
|
||||||
|
|
||||||
|
var ids = {};
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
db.automigrate(setupProducts.bind(null, ids, done));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should merge with default scope', function(done) {
|
||||||
|
Product.active(function(err, products) {
|
||||||
|
should.not.exist(err);
|
||||||
|
products.should.have.length(3);
|
||||||
|
products[0].name.should.equal('Tool Z');
|
||||||
|
products[1].name.should.equal('Widget A');
|
||||||
|
products[2].name.should.equal('Widget Z');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should merge with default scope - tool', function(done) {
|
||||||
|
Tool.active(function(err, products) {
|
||||||
|
should.not.exist(err);
|
||||||
|
products.should.have.length(1);
|
||||||
|
products[0].name.should.equal('Tool Z');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should merge with default scope - widget', function(done) {
|
||||||
|
Widget.active(function(err, products) {
|
||||||
|
should.not.exist(err);
|
||||||
|
products.should.have.length(2);
|
||||||
|
products[0].name.should.equal('Widget A');
|
||||||
|
products[1].name.should.equal('Widget Z');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('scope function', function() {
|
||||||
|
|
||||||
|
before(function(done) {
|
||||||
|
db.automigrate(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a scoped instance - widget', function(done) {
|
||||||
|
Widget.create({name: 'Product', kind:'ignored'}, function(err, p) {
|
||||||
|
p.name.should.equal('Product');
|
||||||
|
p.kind.should.equal('Widget');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a scoped instance - thing', function(done) {
|
||||||
|
Thing.create({name: 'Product', kind:'ignored'}, function(err, p) {
|
||||||
|
p.name.should.equal('Product');
|
||||||
|
p.kind.should.equal('Thing');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find a scoped instance - widget', function(done) {
|
||||||
|
Widget.findOne({where: {name: 'Product'}}, function(err, p) {
|
||||||
|
p.name.should.equal('Product');
|
||||||
|
p.kind.should.equal('Widget');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find a scoped instance - thing', function(done) {
|
||||||
|
Thing.findOne({where: {name: 'Product'}}, function(err, p) {
|
||||||
|
p.name.should.equal('Product');
|
||||||
|
p.kind.should.equal('Thing');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find a scoped instance - thing', function(done) {
|
||||||
|
Product.find({where: {name: 'Product'}}, function(err, products) {
|
||||||
|
products.should.have.length(2);
|
||||||
|
products[0].name.should.equal('Product');
|
||||||
|
products[1].name.should.equal('Product');
|
||||||
|
var kinds = products.map(function(p) { return p.kind; })
|
||||||
|
kinds.sort();
|
||||||
|
kinds.should.eql(['Thing', 'Widget']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('relations', function() {
|
||||||
|
|
||||||
|
var ids = {};
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
db.automigrate(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
Category.create({name: 'Category A'}, function(err, cat) {
|
||||||
|
ids.categoryA = cat.id;
|
||||||
|
async.series([
|
||||||
|
function(next) {
|
||||||
|
cat.widgets.create({name: 'Widget B', kind: 'ignored'}, next);
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
cat.widgets.create({name: 'Widget A'}, next);
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
cat.tools.create({name: 'Tool A'}, next);
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
cat.things.create({name: 'Thing A'}, next);
|
||||||
|
}
|
||||||
|
], done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - products', function(done) {
|
||||||
|
Category.findById(ids.categoryA, function(err, cat) {
|
||||||
|
should.not.exist(err);
|
||||||
|
cat.products(function(err, products) {
|
||||||
|
should.not.exist(err);
|
||||||
|
products.should.have.length(4);
|
||||||
|
products[0].name.should.equal('Thing A');
|
||||||
|
products[1].name.should.equal('Tool A');
|
||||||
|
products[2].name.should.equal('Widget A');
|
||||||
|
products[3].name.should.equal('Widget B');
|
||||||
|
|
||||||
|
products[0].should.be.instanceof(Product);
|
||||||
|
products[0].should.be.instanceof(Thing);
|
||||||
|
|
||||||
|
products[1].should.be.instanceof(Product);
|
||||||
|
products[1].should.be.instanceof(Tool);
|
||||||
|
|
||||||
|
products[2].should.be.instanceof(Product);
|
||||||
|
products[2].should.be.instanceof(Widget);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - widgets', function(done) {
|
||||||
|
Category.findById(ids.categoryA, function(err, cat) {
|
||||||
|
should.not.exist(err);
|
||||||
|
cat.widgets(function(err, products) {
|
||||||
|
should.not.exist(err);
|
||||||
|
products.should.have.length(2);
|
||||||
|
products[0].should.be.instanceof(Widget);
|
||||||
|
products[0].name.should.equal('Widget A');
|
||||||
|
products[1].name.should.equal('Widget B');
|
||||||
|
products[0].category(function(err, inst) {
|
||||||
|
inst.name.should.equal('Category A');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - tools', function(done) {
|
||||||
|
Category.findById(ids.categoryA, function(err, cat) {
|
||||||
|
should.not.exist(err);
|
||||||
|
cat.tools(function(err, products) {
|
||||||
|
should.not.exist(err);
|
||||||
|
products.should.have.length(1);
|
||||||
|
products[0].should.be.instanceof(Tool);
|
||||||
|
products[0].name.should.equal('Tool A');
|
||||||
|
products[0].category(function(err, inst) {
|
||||||
|
inst.name.should.equal('Category A');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default scope - things', function(done) {
|
||||||
|
Category.findById(ids.categoryA, function(err, cat) {
|
||||||
|
should.not.exist(err);
|
||||||
|
cat.things(function(err, products) {
|
||||||
|
should.not.exist(err);
|
||||||
|
products.should.have.length(1);
|
||||||
|
products[0].should.be.instanceof(Thing);
|
||||||
|
products[0].name.should.equal('Thing A');
|
||||||
|
products[0].category(function(err, inst) {
|
||||||
|
inst.name.should.equal('Category A');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create related item with default scope', function(done) {
|
||||||
|
Category.findById(ids.categoryA, function(err, cat) {
|
||||||
|
cat.tools.create({name: 'Tool B'}, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use relation scope order', function(done) {
|
||||||
|
Category.findById(ids.categoryA, function(err, cat) {
|
||||||
|
should.not.exist(err);
|
||||||
|
cat.tools(function(err, products) {
|
||||||
|
should.not.exist(err);
|
||||||
|
products.should.have.length(2);
|
||||||
|
products[0].name.should.equal('Tool B');
|
||||||
|
products[1].name.should.equal('Tool A');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -270,6 +270,48 @@ describe('Memory connector', function () {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should use collection setting', function (done) {
|
||||||
|
var ds = new DataSource({
|
||||||
|
connector: 'memory'
|
||||||
|
});
|
||||||
|
|
||||||
|
var Product = ds.createModel('Product', {
|
||||||
|
name: String
|
||||||
|
});
|
||||||
|
|
||||||
|
var Tool = ds.createModel('Tool', {
|
||||||
|
name: String
|
||||||
|
}, {memory: {collection: 'Product'}});
|
||||||
|
|
||||||
|
var Widget = ds.createModel('Widget', {
|
||||||
|
name: String
|
||||||
|
}, {memory: {collection: 'Product'}});
|
||||||
|
|
||||||
|
ds.connector.getCollection('Tool').should.equal('Product');
|
||||||
|
ds.connector.getCollection('Widget').should.equal('Product');
|
||||||
|
|
||||||
|
async.series([
|
||||||
|
function(next) {
|
||||||
|
Tool.create({ name: 'Tool A' }, next);
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
Tool.create({ name: 'Tool B' }, next);
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
Widget.create({ name: 'Widget A' }, next);
|
||||||
|
}
|
||||||
|
], function(err) {
|
||||||
|
Product.find(function(err, products) {
|
||||||
|
should.not.exist(err);
|
||||||
|
products.should.have.length(3);
|
||||||
|
products[0].toObject().should.eql({ name: 'Tool A', id: 1 });
|
||||||
|
products[1].toObject().should.eql({ name: 'Tool B', id: 2 });
|
||||||
|
products[2].toObject().should.eql({ name: 'Widget A', id: 3 });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -335,7 +335,7 @@ describe('relations', function () {
|
||||||
physician.patients.findById(id, function (err, ch) {
|
physician.patients.findById(id, function (err, ch) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
should.exist(ch);
|
should.exist(ch);
|
||||||
ch.id.should.equal(id);
|
ch.id.should.eql(id);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -387,7 +387,7 @@ describe('relations', function () {
|
||||||
physician.patients.findById(id, function (err, ch) {
|
physician.patients.findById(id, function (err, ch) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
should.exist(ch);
|
should.exist(ch);
|
||||||
ch.id.should.equal(id);
|
ch.id.should.eql(id);
|
||||||
ch.name.should.equal('aa');
|
ch.name.should.equal('aa');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -1659,7 +1659,7 @@ describe('relations', function () {
|
||||||
should.not.exist(e);
|
should.not.exist(e);
|
||||||
should.exist(tags);
|
should.exist(tags);
|
||||||
|
|
||||||
article.tags().should.eql(tags);
|
article.tagNames().should.eql(tags);
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue