Merge branch 'master' of github.com:1602/jugglingdb
This commit is contained in:
commit
0b9454e440
|
@ -37,6 +37,13 @@ AbstractClass.prototype._initProperties = function (data, applySetters) {
|
||||||
var properties = ds.properties;
|
var properties = ds.properties;
|
||||||
data = data || {};
|
data = data || {};
|
||||||
|
|
||||||
|
Object.defineProperty(this, '__cachedRelations', {
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true,
|
||||||
|
value: {}
|
||||||
|
});
|
||||||
|
|
||||||
Object.defineProperty(this, '__data', {
|
Object.defineProperty(this, '__data', {
|
||||||
writable: true,
|
writable: true,
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
|
@ -704,6 +711,23 @@ AbstractClass.hasMany = function hasMany(anotherClass, params) {
|
||||||
*
|
*
|
||||||
* @param {Class} anotherClass - class to belong
|
* @param {Class} anotherClass - class to belong
|
||||||
* @param {Object} params - configuration {as: 'propertyName', foreignKey: 'keyName'}
|
* @param {Object} params - configuration {as: 'propertyName', foreignKey: 'keyName'}
|
||||||
|
*
|
||||||
|
* **Usage examples**
|
||||||
|
* Suppose model Post have a *belongsTo* relationship with User (the author of the post). You could declare it this way:
|
||||||
|
* Post.belongsTo(User, {as: 'author', foreignKey: 'userId'});
|
||||||
|
*
|
||||||
|
* When a post is loaded, you can load the related author with:
|
||||||
|
* post.author(function(err, user) {
|
||||||
|
* // the user variable is your user object
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* The related object is cached, so if later you try to get again the author, no additional request will be made.
|
||||||
|
* But there is an optional boolean parameter in first position that set whether or not you want to reload the cache:
|
||||||
|
* post.author(true, function(err, user) {
|
||||||
|
* // The user is reloaded, even if it was already cached.
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* This optional parameter default value is false, so the related object will be loaded from cache if available.
|
||||||
*/
|
*/
|
||||||
AbstractClass.belongsTo = function (anotherClass, params) {
|
AbstractClass.belongsTo = function (anotherClass, params) {
|
||||||
var methodName = params.as;
|
var methodName = params.as;
|
||||||
|
@ -724,16 +748,39 @@ AbstractClass.belongsTo = function (anotherClass, params) {
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
this.prototype[methodName] = function (p) {
|
this.prototype[methodName] = function (refresh, p) {
|
||||||
|
if (arguments.length === 1) {
|
||||||
|
p = refresh;
|
||||||
|
refresh = false;
|
||||||
|
} else if (arguments.length > 2) {
|
||||||
|
throw new Error('Method can\'t be called with more than two arguments');
|
||||||
|
}
|
||||||
|
var self = this;
|
||||||
|
var cachedValue;
|
||||||
|
if (!refresh && this.__cachedRelations && (typeof this.__cachedRelations[methodName] !== 'undefined')) {
|
||||||
|
cachedValue = this.__cachedRelations[methodName];
|
||||||
|
}
|
||||||
if (p instanceof AbstractClass) { // acts as setter
|
if (p instanceof AbstractClass) { // acts as setter
|
||||||
this[fk] = p.id;
|
this[fk] = p.id;
|
||||||
|
this.__cachedRelations[methodName] = p;
|
||||||
} else if (typeof p === 'function') { // acts as async getter
|
} else if (typeof p === 'function') { // acts as async getter
|
||||||
this.__finders__[methodName](this[fk], p);
|
if (typeof cachedValue === 'undefined') {
|
||||||
return this[fk];
|
this.__finders__[methodName](this[fk], function(err, inst) {
|
||||||
|
if (!err) {
|
||||||
|
self.__cachedRelations[methodName] = inst;
|
||||||
|
}
|
||||||
|
p(err, inst);
|
||||||
|
});
|
||||||
|
return this[fk];
|
||||||
|
} else {
|
||||||
|
p(null, cachedValue);
|
||||||
|
return cachedValue;
|
||||||
|
}
|
||||||
} else if (typeof p === 'undefined') { // acts as sync getter
|
} else if (typeof p === 'undefined') { // acts as sync getter
|
||||||
return this[fk];
|
return this[fk];
|
||||||
} else { // setter
|
} else { // setter
|
||||||
this[fk] = p;
|
this[fk] = p;
|
||||||
|
delete this.__cachedRelations[methodName];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -768,18 +815,35 @@ function defineScope(cls, targetClass, name, params, methods) {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
get: function () {
|
get: function () {
|
||||||
var f = function caller(cond, cb) {
|
var f = function caller(condOrRefresh, cb) {
|
||||||
var actualCond;
|
var actualCond = {};
|
||||||
|
var actualRefresh = false;
|
||||||
|
var saveOnCache = true;
|
||||||
if (arguments.length === 1) {
|
if (arguments.length === 1) {
|
||||||
actualCond = {};
|
cb = condOrRefresh;
|
||||||
cb = cond;
|
|
||||||
} else if (arguments.length === 2) {
|
} else if (arguments.length === 2) {
|
||||||
actualCond = cond;
|
if (typeof condOrRefresh === 'boolean') {
|
||||||
|
actualRefresh = condOrRefresh;
|
||||||
|
} else {
|
||||||
|
actualCond = condOrRefresh;
|
||||||
|
actualRefresh = true;
|
||||||
|
saveOnCache = false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Method only can be called with one or two arguments');
|
throw new Error('Method can be only called with one or two arguments');
|
||||||
}
|
}
|
||||||
|
|
||||||
return targetClass.all(mergeParams(actualCond, caller._scope), cb);
|
if (!this.__cachedRelations || (typeof this.__cachedRelations[name] == 'undefined') || actualRefresh) {
|
||||||
|
var self = this;
|
||||||
|
return targetClass.all(mergeParams(actualCond, caller._scope), function(err, data) {
|
||||||
|
if (!err && saveOnCache) {
|
||||||
|
self.__cachedRelations[name] = data;
|
||||||
|
}
|
||||||
|
cb(err, data);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
cb(null, this.__cachedRelations[name]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
f._scope = typeof params === 'function' ? params.call(this) : params;
|
f._scope = typeof params === 'function' ? params.call(this) : params;
|
||||||
f.build = build;
|
f.build = build;
|
||||||
|
|
|
@ -26,6 +26,7 @@ var schemas = {
|
||||||
|
|
||||||
var specificTest = getSpecificTests();
|
var specificTest = getSpecificTests();
|
||||||
var testPerformed = false;
|
var testPerformed = false;
|
||||||
|
var nbSchemaRequests = 0;
|
||||||
|
|
||||||
Object.keys(schemas).forEach(function (schemaName) {
|
Object.keys(schemas).forEach(function (schemaName) {
|
||||||
if (process.env.ONLY && process.env.ONLY !== schemaName) return;
|
if (process.env.ONLY && process.env.ONLY !== schemaName) return;
|
||||||
|
@ -48,7 +49,8 @@ function performTestFor(schemaName) {
|
||||||
});
|
});
|
||||||
|
|
||||||
schema.log = function (a) {
|
schema.log = function (a) {
|
||||||
console.log(a);
|
console.log(a);
|
||||||
|
nbSchemaRequests++;
|
||||||
};
|
};
|
||||||
|
|
||||||
testOrm(schema);
|
testOrm(schema);
|
||||||
|
@ -446,6 +448,58 @@ function testOrm(schema) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('hasMany should be cached', function (test) {
|
||||||
|
|
||||||
|
User.find(1, function(err, user) {
|
||||||
|
User.create(function(err, voidUser) {
|
||||||
|
Post.create({userId: user.id}, function() {
|
||||||
|
|
||||||
|
// There can't be any concurrency because we are counting requests
|
||||||
|
// We are first testing cases when user has posts
|
||||||
|
user.posts(function(err, data) {
|
||||||
|
var nbInitialRequests = nbSchemaRequests;
|
||||||
|
user.posts(function(err, data2) {
|
||||||
|
test.equal(data.length, 2, 'There should be 2 posts.');
|
||||||
|
test.equal(data.length, data2.length, 'Posts should be the same, since we are loading on the same object.');
|
||||||
|
test.equal(nbInitialRequests, nbSchemaRequests, 'There should not be any request because value is cached.');
|
||||||
|
|
||||||
|
user.posts({where: {id: 12}}, function(err, data) {
|
||||||
|
test.equal(data.length, 1, 'There should be only one post.');
|
||||||
|
test.equal(nbInitialRequests + 1, nbSchemaRequests, 'There should be one additional request since we added conditions.');
|
||||||
|
|
||||||
|
user.posts(function(err, data) {
|
||||||
|
test.equal(data.length, 2, 'Previous get shouldn\'t have changed cached value though, since there was additional conditions.');
|
||||||
|
test.equal(nbInitialRequests + 1, nbSchemaRequests, 'There should not be any request because value is cached.');
|
||||||
|
|
||||||
|
// We are now testing cases when user doesn't have any post
|
||||||
|
voidUser.posts(function(err, data) {
|
||||||
|
var nbInitialRequests = nbSchemaRequests;
|
||||||
|
voidUser.posts(function(err, data2) {
|
||||||
|
test.equal(data.length, 0, 'There shouldn\'t be any posts (1/2).');
|
||||||
|
test.equal(data2.length, 0, 'There shouldn\'t be any posts (2/2).');
|
||||||
|
test.equal(nbInitialRequests, nbSchemaRequests, 'There should not be any request because value is cached.');
|
||||||
|
|
||||||
|
voidUser.posts(true, function(err, data3) {
|
||||||
|
test.equal(data3.length, 0, 'There shouldn\'t be any posts.');
|
||||||
|
test.equal(nbInitialRequests + 1, nbSchemaRequests, 'There should be one additional request since we forced refresh.');
|
||||||
|
|
||||||
|
test.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
// it('should handle hasOne relationship', function (test) {
|
// it('should handle hasOne relationship', function (test) {
|
||||||
// User.create(function (err, u) {
|
// User.create(function (err, u) {
|
||||||
// if (err) return console.log(err);
|
// if (err) return console.log(err);
|
||||||
|
@ -882,6 +936,48 @@ function testOrm(schema) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('belongsTo should be cached', function (test) {
|
||||||
|
User.findOne(function(err, user) {
|
||||||
|
|
||||||
|
var passport = new Passport({ownerId: user.id});
|
||||||
|
var passport2 = new Passport({ownerId: null});
|
||||||
|
|
||||||
|
// There can't be any concurrency because we are counting requests
|
||||||
|
// We are first testing cases when passport has an owner
|
||||||
|
passport.owner(function(err, data) {
|
||||||
|
var nbInitialRequests = nbSchemaRequests;
|
||||||
|
passport.owner(function(err, data2) {
|
||||||
|
test.equal(data.id, data2.id, 'The value should remain the same');
|
||||||
|
test.equal(nbInitialRequests, nbSchemaRequests, 'There should not be any request because value is cached.');
|
||||||
|
|
||||||
|
// We are now testing cases when passport has not an owner
|
||||||
|
passport2.owner(function(err, data) {
|
||||||
|
var nbInitialRequests2 = nbSchemaRequests;
|
||||||
|
passport2.owner(function(err, data2) {
|
||||||
|
test.equal(data, null, 'The value should be null since there is no owner');
|
||||||
|
test.equal(data, data2, 'The value should remain the same (null)');
|
||||||
|
test.equal(nbInitialRequests2, nbSchemaRequests, 'There should not be any request because value is cached.');
|
||||||
|
|
||||||
|
passport2.owner(user.id);
|
||||||
|
passport2.owner(function(err, data3) {
|
||||||
|
test.equal(data3.id, user.id, 'Owner should now be the user.');
|
||||||
|
test.equal(nbInitialRequests2 + 1, nbSchemaRequests, 'If we changed owner id, there should be one more request.');
|
||||||
|
|
||||||
|
passport2.owner(true, function(err, data4) {
|
||||||
|
test.equal(data3.id, data3.id, 'The value should remain the same');
|
||||||
|
test.equal(nbInitialRequests2 + 2, nbSchemaRequests, 'If we forced refreshing, there should be one more request.');
|
||||||
|
test.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
if (schema.name !== 'mongoose' && schema.name !== 'neo4j')
|
if (schema.name !== 'mongoose' && schema.name !== 'neo4j')
|
||||||
it('should update or create record', function (test) {
|
it('should update or create record', function (test) {
|
||||||
var newData = {
|
var newData = {
|
||||||
|
|
Loading…
Reference in New Issue