Clean up scope implementation
This commit is contained in:
parent
2db43c58e5
commit
046816191d
208
lib/scope.js
208
lib/scope.js
|
@ -6,6 +6,52 @@ var defineCachedRelations = utils.defineCachedRelations;
|
|||
*/
|
||||
exports.defineScope = defineScope;
|
||||
|
||||
function ScopeDefinition(definition) {
|
||||
this.sourceModel = definition.sourceModel;
|
||||
this.targetModel = definition.targetModel || definition.sourceModel;
|
||||
this.name = definition.name;
|
||||
this.params = definition.params;
|
||||
this.methods = definition.methods;
|
||||
}
|
||||
|
||||
ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefresh, cb) {
|
||||
var name = this.name;
|
||||
var self = receiver;
|
||||
|
||||
var actualCond = {};
|
||||
var actualRefresh = false;
|
||||
var saveOnCache = true;
|
||||
if (arguments.length === 3) {
|
||||
cb = condOrRefresh;
|
||||
} else if (arguments.length === 4) {
|
||||
if (typeof condOrRefresh === 'boolean') {
|
||||
actualRefresh = condOrRefresh;
|
||||
} else {
|
||||
actualCond = condOrRefresh;
|
||||
actualRefresh = true;
|
||||
saveOnCache = false;
|
||||
}
|
||||
} else {
|
||||
throw new Error('Method can be only called with one or two arguments');
|
||||
}
|
||||
|
||||
if (!self.__cachedRelations || self.__cachedRelations[name] === undefined
|
||||
|| actualRefresh) {
|
||||
// It either doesn't hit the cache or refresh is required
|
||||
var params = mergeParams(actualCond, scopeParams);
|
||||
return this.targetModel.find(params, function (err, data) {
|
||||
if (!err && saveOnCache) {
|
||||
defineCachedRelations(self);
|
||||
self.__cachedRelations[name] = data;
|
||||
}
|
||||
cb(err, data);
|
||||
});
|
||||
} else {
|
||||
// Return from cache
|
||||
cb(null, self.__cachedRelations[name]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a scope to the class
|
||||
* @param {Model} cls The class where the scope method is added
|
||||
|
@ -22,7 +68,7 @@ function defineScope(cls, targetClass, name, params, methods) {
|
|||
cls._scopeMeta = {};
|
||||
}
|
||||
|
||||
// only makes sence to add scope in meta if base and target classes
|
||||
// only makes sense to add scope in meta if base and target classes
|
||||
// are same
|
||||
if (cls === targetClass) {
|
||||
cls._scopeMeta[name] = params;
|
||||
|
@ -32,6 +78,14 @@ function defineScope(cls, targetClass, name, params, methods) {
|
|||
}
|
||||
}
|
||||
|
||||
var definition = new ScopeDefinition({
|
||||
sourceModel: cls,
|
||||
targetModel: targetClass,
|
||||
name: name,
|
||||
params: params,
|
||||
methods: methods
|
||||
});
|
||||
|
||||
// Define a property for the scope
|
||||
Object.defineProperty(cls, name, {
|
||||
enumerable: false,
|
||||
|
@ -49,42 +103,18 @@ function defineScope(cls, targetClass, name, params, methods) {
|
|||
*/
|
||||
get: function () {
|
||||
var self = this;
|
||||
var f = function caller(condOrRefresh, cb) {
|
||||
var actualCond = {};
|
||||
var actualRefresh = false;
|
||||
var saveOnCache = true;
|
||||
if (arguments.length === 1) {
|
||||
cb = condOrRefresh;
|
||||
} else if (arguments.length === 2) {
|
||||
if (typeof condOrRefresh === 'boolean') {
|
||||
actualRefresh = condOrRefresh;
|
||||
} else {
|
||||
actualCond = condOrRefresh;
|
||||
actualRefresh = true;
|
||||
saveOnCache = false;
|
||||
}
|
||||
var f = function(condOrRefresh, cb) {
|
||||
if(arguments.length === 1) {
|
||||
definition.related(self, f._scope, condOrRefresh);
|
||||
} else {
|
||||
throw new Error('Method can be only called with one or two arguments');
|
||||
}
|
||||
|
||||
if (!self.__cachedRelations || self.__cachedRelations[name] === undefined
|
||||
|| actualRefresh) {
|
||||
// It either doesn't hit the cache or reresh is required
|
||||
var params = mergeParams(actualCond, caller._scope);
|
||||
return targetClass.find(params, function (err, data) {
|
||||
if (!err && saveOnCache) {
|
||||
defineCachedRelations(self);
|
||||
self.__cachedRelations[name] = data;
|
||||
}
|
||||
cb(err, data);
|
||||
});
|
||||
} else {
|
||||
// Return from cache
|
||||
cb(null, self.__cachedRelations[name]);
|
||||
definition.related(self, f._scope, condOrRefresh, cb);
|
||||
}
|
||||
};
|
||||
f._scope = typeof params === 'function' ? params.call(this) : params;
|
||||
f._targetClass = targetClass.modelName;
|
||||
|
||||
f._scope = typeof definition.params === 'function' ?
|
||||
definition.params.call(self) : definition.params;
|
||||
|
||||
f._targetClass = definition.targetModel.modelName;
|
||||
if (f._scope.collect) {
|
||||
f._targetClass = i8n.capitalize(f._scope.collect);
|
||||
}
|
||||
|
@ -92,11 +122,14 @@ function defineScope(cls, targetClass, name, params, methods) {
|
|||
f.build = build;
|
||||
f.create = create;
|
||||
f.destroyAll = destroyAll;
|
||||
for (var i in methods) {
|
||||
f[i] = methods[i].bind(this);
|
||||
for (var i in definition.methods) {
|
||||
f[i] = definition.methods[i].bind(self);
|
||||
}
|
||||
|
||||
// define sub-scopes
|
||||
// Define scope-chaining, such as
|
||||
// Station.scope('active', {where: {isActive: true}});
|
||||
// Station.scope('subway', {where: {isUndeground: true}});
|
||||
// Station.active.subway(cb);
|
||||
Object.keys(targetClass._scopeMeta).forEach(function (name) {
|
||||
Object.defineProperty(f, name, {
|
||||
enumerable: false,
|
||||
|
@ -105,7 +138,7 @@ function defineScope(cls, targetClass, name, params, methods) {
|
|||
return f;
|
||||
}
|
||||
});
|
||||
}.bind(this));
|
||||
}.bind(self));
|
||||
return f;
|
||||
}
|
||||
});
|
||||
|
@ -152,7 +185,13 @@ function defineScope(cls, targetClass, name, params, methods) {
|
|||
|
||||
// and it should have create/build methods with binded thisModelNameId param
|
||||
function build(data) {
|
||||
return new targetClass(mergeParams(this._scope, {where: data || {}}).where);
|
||||
data = data || {};
|
||||
var params = mergeParams(this._scope, {where: data}).where;
|
||||
delete params['and'];
|
||||
delete params['or'];
|
||||
delete params['nor'];
|
||||
|
||||
return new targetClass(params);
|
||||
}
|
||||
|
||||
function create(data, cb) {
|
||||
|
@ -187,53 +226,70 @@ function defineScope(cls, targetClass, name, params, methods) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
function mergeParams(base, update) {
|
||||
base = base || {};
|
||||
if (update.where) {
|
||||
base.where = merge(base.where, update.where);
|
||||
}
|
||||
if (update.include) {
|
||||
base.include = update.include;
|
||||
}
|
||||
if (update.collect) {
|
||||
base.collect = update.collect;
|
||||
}
|
||||
|
||||
// overwrite order
|
||||
if (update.order) {
|
||||
base.order = update.order;
|
||||
}
|
||||
|
||||
if(update.limit !== undefined) {
|
||||
base.limit = update.limit;
|
||||
}
|
||||
if(update.skip !== undefined) {
|
||||
base.skip = update.skip;
|
||||
}
|
||||
if(update.offset !== undefined) {
|
||||
base.offset = update.offset;
|
||||
}
|
||||
if(update.fields !== undefined) {
|
||||
base.fields = update.fields;
|
||||
}
|
||||
return base;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/*!
|
||||
* Merge `base` and `update` params
|
||||
* @param {Object} base - base object (updating this object)
|
||||
* @param {Object} update - object with new data to update base
|
||||
* @returns {Object} `base`
|
||||
* @private
|
||||
*/
|
||||
function merge(base, update) {
|
||||
function mergeWhere(base, update) {
|
||||
base = base || {};
|
||||
if (update) {
|
||||
Object.keys(update).forEach(function (key) {
|
||||
var keys = Object.keys(update);
|
||||
for (var k = 0; k < keys.length; k++) {
|
||||
var key = keys[k];
|
||||
base[key] = update[key];
|
||||
});
|
||||
}
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Merge query parameters
|
||||
* @param base
|
||||
* @param update
|
||||
* @returns {*|{}}
|
||||
* @private
|
||||
*/
|
||||
function mergeParams(base, update) {
|
||||
if (!update) {
|
||||
return;
|
||||
}
|
||||
base = base || {};
|
||||
if (update.where) {
|
||||
base.where = mergeWhere(base.where, update.where);
|
||||
}
|
||||
|
||||
// Overwrite inclusion
|
||||
if (update.include) {
|
||||
base.include = update.include;
|
||||
}
|
||||
if (update.collect) {
|
||||
base.collect = update.collect;
|
||||
}
|
||||
|
||||
// overwrite order
|
||||
if (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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue