base-domain
Version:
simple module to help build Domain-Driven Design
321 lines (266 loc) • 8.3 kB
JavaScript
'use strict';
var BaseDict, BaseList, GeneralFactory, Util,
hasProp = {}.hasOwnProperty;
BaseList = require('./base-list');
BaseDict = require('./base-dict');
Util = require('../util');
/**
general factory class
create instance of model
@class GeneralFactory
@implements FactoryInterface
@module base-domain
*/
GeneralFactory = (function() {
/**
create a factory.
If specific factory is defined, return the instance.
Otherwise, return instance of GeneralFactory.
This method is not suitable for creating collections, thus only called by Repository, which handles Entity (= non-collection).
@method create
@static
@param {String} modelName
@param {RootInterface} root
@return {FactoryInterface}
*/
GeneralFactory.create = function(modelName, root) {
var e;
try {
return root.createPreferredFactory(modelName);
} catch (error) {
e = error;
return new GeneralFactory(modelName, root);
}
};
/**
create an instance of the given modelName using obj
if obj is null, return null
if obj is undefined, empty object is created.
@method createModel
@param {String} modelName
@param {Object} obj
@param {Object} [options]
@param {Object} [options.include] options to pass to Includer
@param {Object} [options.include.async=false] include sub-entities asynchronously if true.
@param {Array(String)} [options.include.props] include sub-entities of given props
@param {RootInterface} root
@return {BaseModel}
*/
GeneralFactory.createModel = function(modelName, obj, options, root) {
var Model;
if (obj === null) {
return null;
}
Model = root.getModule().getModel(modelName);
if (Model.prototype instanceof BaseList) {
return this.create(Model.itemModelName, root).createList(modelName, obj, options);
} else if (Model.prototype instanceof BaseDict) {
return this.create(Model.itemModelName, root).createDict(modelName, obj, options);
} else {
return this.create(modelName, root).createFromObject(obj != null ? obj : {}, options);
}
};
/**
constructor
@constructor
@param {String} modelName
@param {RootInterface} root
*/
function GeneralFactory(modelName1, root1) {
this.modelName = modelName1;
this.root = root1;
this.facade = this.root.facade;
this.modelProps = this.facade.getModelProps(this.root.getModule().normalizeName(this.modelName));
}
/**
get model class this factory handles
@method getModelClass
@return {Function}
*/
GeneralFactory.prototype.getModelClass = function() {
return this.root.getModule().getModel(this.modelName);
};
/**
create empty model instance
@method createEmpty
@public
@return {BaseModel}
*/
GeneralFactory.prototype.createEmpty = function() {
return this.createFromObject({});
};
/**
create instance of model class by plain object
for each prop, values are set by Model#set(prop, value)
@method createFromObject
@public
@param {Object} obj
@param {Object} [options={}]
@param {Object} [options.include] options to pass to Includer
@param {Object} [options.include.async=false] include sub-entities asynchronously if true.
@param {Array(String)} [options.include.props] include sub-entities of given props
@return {BaseModel} model
*/
GeneralFactory.prototype.createFromObject = function(obj, options) {
var ModelClass, defaultValue, i, len, model, prop, ref, subModelName, value;
if (options == null) {
options = {};
}
ModelClass = this.getModelClass();
if (obj instanceof ModelClass) {
return obj;
}
if ((obj == null) || typeof obj !== 'object') {
return null;
}
model = this.create();
for (prop in obj) {
if (!hasProp.call(obj, prop)) continue;
value = obj[prop];
if ((value == null) && this.modelProps.isOptional(prop)) {
continue;
}
if (subModelName = this.modelProps.getSubModelName(prop)) {
value = this.constructor.createModel(subModelName, value, options, this.root);
}
model.set(prop, value);
}
ref = this.modelProps.getAllProps();
for (i = 0, len = ref.length; i < len; i++) {
prop = ref[i];
if ((model[prop] != null) || obj.hasOwnProperty(prop)) {
continue;
}
if (this.modelProps.isId(prop)) {
continue;
}
if (this.modelProps.isOptional(prop)) {
continue;
}
defaultValue = this.modelProps.getDefaultValue(prop);
if (subModelName = this.modelProps.getSubModelName(prop)) {
if (this.modelProps.isEntity(prop)) {
continue;
}
model.set(prop, this.constructor.createModel(subModelName, defaultValue, options, this.root));
} else if (defaultValue != null) {
switch (typeof defaultValue) {
case 'object':
defaultValue = Util.clone(defaultValue);
break;
case 'function':
defaultValue = defaultValue();
}
model.set(prop, defaultValue);
} else {
model.set(prop, void 0);
}
}
if (options.include !== null) {
this.include(model, options.include).then((function(_this) {
return function(model) {
if (model.constructor.isImmutable) {
return model.freeze();
} else {
return model;
}
};
})(this));
} else if (model.constructor.isImmutable) {
return model.freeze();
}
return model;
};
/**
include submodels
@method include
@private
@param {BaseModel} model
@param {Object} [includeOptions]
@param {Object} [includeOptions.async=false] include submodels asynchronously
@param {Array(String)} [includeOptions.props] include submodels of given props
*/
GeneralFactory.prototype.include = function(model, includeOptions) {
if (includeOptions == null) {
includeOptions = {};
}
if (includeOptions.async == null) {
includeOptions.async = false;
}
if (!includeOptions) {
return Promise.resolve(model);
}
return model.include(includeOptions);
};
/**
create model list
@method createList
@public
@param {String} listModelName model name of list
@param {any} val
@param {Object} [options]
@param {Object} [options.include] options to pass to Includer
@param {Object} [options.include.async=false] include sub-entities asynchronously if true.
@param {Array(String)} [options.include.props] include sub-entities of given props
@return {BaseList} list
*/
GeneralFactory.prototype.createList = function(listModelName, val, options) {
return this.createCollection(listModelName, val, options);
};
/**
create model dict
@method createDict
@public
@param {String} dictModelName model name of dict
@param {any} val
@param {Object} [options]
@param {Object} [options.include] options to pass to Includer
@param {Object} [options.include.async=false] include sub-entities asynchronously if true.
@param {Array(String)} [options.include.props] include sub-entities of given props
@return {BaseDict} dict
*/
GeneralFactory.prototype.createDict = function(dictModelName, val, options) {
return this.createCollection(dictModelName, val, options);
};
/**
create collection
@method createCollection
@private
@param {String} collModelName model name of collection
@param {any} val
@param {Object} [options]
@return {BaseDict} dict
*/
GeneralFactory.prototype.createCollection = function(collModelName, val, options) {
if (val === null) {
return null;
}
if (val == null) {
val = [];
}
if (Array.isArray(val)) {
if (typeof val[0] === 'object') {
val = {
items: val
};
} else {
val = {
ids: val
};
}
}
return new GeneralFactory(collModelName, this.root).createFromObject(val, options);
};
/**
create an empty model
@protected
@return {BaseModel}
*/
GeneralFactory.prototype.create = function() {
var Model;
Model = this.getModelClass();
return new Model(null, this.root);
};
return GeneralFactory;
})();
module.exports = GeneralFactory;