diff --git a/lib/abstract-class.js b/lib/abstract-class.js index 960b4e73..65392c4b 100644 --- a/lib/abstract-class.js +++ b/lib/abstract-class.js @@ -37,6 +37,13 @@ AbstractClass.prototype._initProperties = function (data, applySetters) { var properties = ds.properties; data = data || {}; + Object.defineProperty(this, '__cachedRelations', { + writable: true, + enumerable: false, + configurable: true, + value: {} + }); + Object.defineProperty(this, '__data', { writable: true, enumerable: false, @@ -723,15 +730,32 @@ AbstractClass.belongsTo = function (anotherClass, params) { }; this.prototype[methodName] = function (p) { + var self = this; + var cachedValue = null; + if (this.__cachedRelations && this.__cachedRelations[methodName]) { + cachedValue = this.__cachedRelations[methodName]; + } if (p instanceof AbstractClass) { // acts as setter this[fk] = p.id; + this.__cachedRelations[methodName] = p; } else if (typeof p === 'function') { // acts as async getter - this.__finders__[methodName](this[fk], p); - return this[fk]; + if (cachedValue === null) { + 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 - return this[fk]; + return cachedValue || this[fk]; } else { // setter this[fk] = p; + this.__cachedRelations[methodName] = p; } }; @@ -766,18 +790,34 @@ function defineScope(cls, targetClass, name, params, methods) { enumerable: false, configurable: true, get: function () { - var f = function caller(cond, cb) { - var actualCond; + var f = function caller(condOrRefresh, cb) { + var actualCond = {}; + var actualRefresh = false; if (arguments.length === 1) { - actualCond = {}; - cb = cond; + cb = condOrRefresh; } else if (arguments.length === 2) { - actualCond = cond; - } else { - throw new Error('Method only can be called with one or two arguments'); + if (typeof condOrRefresh === 'boolean') { + actualRefresh = condOrRefresh; + } else { + actualCond = condOrRefresh; + } + } else if (arguments.length > 2) { + throw new Error('Method only can\'t be called with more than two arguments'); } - return targetClass.all(mergeParams(actualCond, caller._scope), cb); + if (!this.__cachedRelations || !this.__cachedRelations[name] || actualRefresh) { + var self = this; + return targetClass.all(mergeParams(actualCond, caller._scope), function(err, data) { + self.__cachedRelations[name] = data; + cb(err, data); + }); + } else { + if (cb) { + cb(null, this.__cachedRelations); + } else { + return this.__cachedRelations[name]; + } + } }; f._scope = typeof params === 'function' ? params.call(this) : params; f.build = build;