Properly reset Memory connector cache on automigrate

This commit is contained in:
Fabien Franzen 2014-09-06 14:38:57 +02:00
parent 8352ed3afc
commit ad55681d69
6 changed files with 259 additions and 33 deletions

View File

@ -78,6 +78,11 @@ Memory.prototype.getCollection = function(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;
@ -181,10 +186,7 @@ Memory.prototype.saveToFile = function (result, callback) {
Memory.prototype.define = function defineModel(definition) {
this.constructor.super_.prototype.define.apply(this, [].slice.call(arguments));
var m = definition.model.modelName;
if(!this.collection(m)) {
this.collection(m, {});
this.collectionSeq(m, 1);
}
if(!this.collection(m)) this.initCollection(m);
};
Memory.prototype.create = function create(model, data, callback) {
@ -612,6 +614,16 @@ Memory.prototype.buildNearFilter = function (filter) {
// 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) {
if (!base) {
return update;

View File

@ -79,7 +79,10 @@ DataAccessObject.applyProperties = function(data) {
};
DataAccessObject.applyScope = function(cond) {
var scope = this.definition.settings.scope;
if (typeof scope === 'object') {
mergeQuery(cond, scope || {}, this.definition.settings.scoping);
}
};
/**
@ -330,17 +333,7 @@ DataAccessObject.exists = function exists(id, cb) {
*/
DataAccessObject.findById = function find(id, cb) {
if (stillConnecting(this.getDataSource(), this, arguments)) return;
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));
this.findOne(byIdQuery(this, id), cb);
};
DataAccessObject.findByIds = function(ids, cond, cb) {
@ -702,7 +695,7 @@ DataAccessObject.find = function find(query, cb) {
var self = this;
query = query || {};
try {
this._normalize(query);
} catch (err) {
@ -711,6 +704,8 @@ DataAccessObject.find = function find(query, cb) {
});
}
this.applyScope(query);
var near = query && geo.nearFilter(query.where);
var supportsGeo = !!this.getDataSource().connector.buildNearFilter;

View File

@ -134,7 +134,7 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
}
if (properties[p]) {
// Managed property
if (applySetters) {
if (applySetters || properties[p].id) {
self[p] = propVal;
} else {
self.__data[p] = propVal;

View File

@ -56,14 +56,17 @@ function setScopeValuesFromWhere(data, where, targetModel) {
* 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) {
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]};
@ -73,7 +76,7 @@ function mergeQuery(base, update) {
}
// Merge inclusion
if (update.include) {
if (spec.include !== false && update.include) {
if (!base.include) {
base.include = update.include;
} else {
@ -82,30 +85,36 @@ function mergeQuery(base, update) {
base.include[update.include] = saved;
}
}
if (update.collect) {
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 && update.order) {
if ((!base.order || spec.order === false) && update.order) {
base.order = update.order;
}
// overwrite pagination
if (update.limit !== undefined) {
if (spec.limit !== false && update.limit !== undefined) {
base.limit = update.limit;
}
if (update.skip !== undefined) {
var skip = spec.skip !== false && spec.offset !== false;
if (skip && update.skip !== undefined) {
base.skip = update.skip;
}
if (update.offset !== undefined) {
if (skip && update.offset !== undefined) {
base.offset = update.offset;
}
// Overwrite fields
if (update.fields !== undefined) {
base.fields = update.fields;
}
return base;
}

210
test/default-scope.test.js Normal file
View File

@ -0,0 +1,210 @@
// This test written in mocha+should.js
var should = require('./init.js');
var async = require('async');
var db, Product, Tool, Widget;
// This test requires a connector that can
// handle a custom collection or table name
describe('default scope', function () {
before(function (done) {
db = getSchema();
Product = db.define('Product', {
name: String,
kind: String,
description: String
}, {
scope: { order: 'name' },
});
Tool = db.define('Tool', {
name: String,
kind: String,
description: String
}, {
base: 'Product',
scope: { where: { kind: 'tool' }, order: 'name' },
mongodb: { collection: 'Product' },
memory: { collection: 'Product' }
});
Widget = db.define('Widget', {
name: String,
kind: String,
description: String
}, {
base: 'Product',
scope: { where: { kind: 'widget' }, order: 'name' },
mongodb: { collection: 'Product' },
memory: { collection: 'Product' }
});
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('queries', function() {
var ids = {};
before(function (done) {
db.automigrate(function(err) {
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'}, function(err, inst) {
ids.toolA = inst.id;
next();
});
},
function(next) {
Widget.create({name: 'Widget A'}, function(err, inst) {
ids.widgetA = inst.id;
next();
});
}
], done);
});
});
it('should apply default scope - order', function(done) {
Product.find(function(err, products) {
should.not.exist(err);
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 A');
products[3].name.should.equal('Widget Z');
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(4);
products[0].name.should.equal('Widget Z');
products[1].name.should.equal('Widget A');
products[2].name.should.equal('Tool Z');
products[3].name.should.equal('Tool A');
done();
});
});
it('should apply default scope - where + order (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 + order (widget)', function(done) {
Widget.find({ order: 'name DESC' }, function(err, products) {
should.not.exist(err);
products.should.have.length(2);
products[0].name.should.equal('Widget Z');
products[1].name.should.equal('Widget A');
done();
});
});
});
});

View File

@ -335,7 +335,7 @@ describe('relations', function () {
physician.patients.findById(id, function (err, ch) {
should.not.exist(err);
should.exist(ch);
ch.id.should.equal(id);
ch.id.should.eql(id);
done();
});
}
@ -387,7 +387,7 @@ describe('relations', function () {
physician.patients.findById(id, function (err, ch) {
should.not.exist(err);
should.exist(ch);
ch.id.should.equal(id);
ch.id.should.eql(id);
ch.name.should.equal('aa');
done();
});