base-domain
Version:
simple module to help build Domain-Driven Design
308 lines (270 loc) • 8.57 kB
JavaScript
var Fixture, FixtureModel, fs, normalize,
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
normalize = require('path').normalize;
fs = require('fs');
/**
load data from directory and generates fixtures
@class Fixture
@module base-domain
*/
Fixture = (function() {
/**
@constructor
@param {Object} [options]
@param {String|Array} [options.dirname='./fixtures'] director(y|ies) to have fixture files. /data, /tsvs should be included in the directory.
@param {Object} [options.data={}] default data, merged to dataPool
@param {String} [options.debug] if true, shows debug log
*/
function Fixture(facade, options) {
var base, dataDir, dirname, dirnames, ext, file, j, k, len, len1, modelName, ref, ref1, ref2, setting;
this.facade = facade;
if (options == null) {
options = {};
}
this.debug = (ref = options.debug) != null ? ref : !!this.facade.debug;
this.fxModelMap = {};
dirnames = options.dirname != null ? Array.isArray(options.dirname) ? options.dirname : [options.dirname] : [__dirname + '/fixtures'];
for (j = 0, len = dirnames.length; j < len; j++) {
dirname = dirnames[j];
dataDir = normalize(dirname + '/data');
ref1 = fs.readdirSync(dataDir);
for (k = 0, len1 = ref1.length; k < len1; k++) {
file = ref1[k];
ref2 = file.split('.'), modelName = ref2[0], ext = ref2[1];
if (ext !== 'js' && ext !== 'coffee' && ext !== 'json') {
continue;
}
setting = require(dataDir + '/' + file);
this.fxModelMap[modelName] = new FixtureModel(this, modelName, setting, normalize(dirname));
}
}
this.dataPool = (options.data != null) && typeof options.data === 'object' ? JSON.parse(JSON.stringify(options.data)) : {};
for (modelName in this.fxModelMap) {
if ((base = this.dataPool)[modelName] == null) {
base[modelName] = {};
}
}
}
/**
add data to pool for model's data
@method addToDataPool
@return {Object}
*/
Fixture.prototype.addToDataPool = function(modelName, dataName, data) {
return this.dataPool[modelName][dataName] = data;
};
/**
inserts data to datasource
@param {Array} names list of fixture models to insert data
@method insert
@return {Promise}
*/
Fixture.prototype.insert = function(names) {
var insert, modelNames, name;
if (names == null) {
names = (function() {
var results;
results = [];
for (name in this.fxModelMap) {
results.push(name);
}
return results;
}).call(this);
}
if (typeof names === 'string') {
names = [names];
}
modelNames = this.resolveDependencies(names);
if (!modelNames.length) {
if (this.debug) {
console.log('no data to insert.');
}
return Promise.resolve(true);
}
if (this.debug) {
console.log('insertion order');
}
if (this.debug) {
console.log("\t" + (modelNames.join(' -> ')) + "\n");
}
return (insert = (function(_this) {
return function() {
var fxModel, modelName;
modelName = modelNames.shift();
if (modelName == null) {
return Promise.resolve(true);
}
fxModel = _this.fxModelMap[modelName];
return fxModel.insert().then(function() {
return insert();
})["catch"](function(e) {
console.error(e.stack);
return false;
});
};
})(this))();
};
/**
adds dependent models, topological sort
@private
@param {Array} names list of fixture models to insert data
@method resolveDependencies
@return {Array} model names
*/
Fixture.prototype.resolveDependencies = function(names) {
var add, el, j, k, len, len1, namesWithDependencies, sortedNames, visit, visited;
namesWithDependencies = [];
for (j = 0, len = names.length; j < len; j++) {
el = names[j];
(add = (function(_this) {
return function(name) {
var depname, fxModel, k, len1, ref, results;
if (indexOf.call(namesWithDependencies, name) >= 0) {
return;
}
namesWithDependencies.push(name);
fxModel = _this.fxModelMap[name];
if (!fxModel) {
throw new Error("model '" + name + "' is not found. It might be written in some 'dependencies' property.");
}
ref = fxModel.dependencies;
results = [];
for (k = 0, len1 = ref.length; k < len1; k++) {
depname = ref[k];
results.push(add(depname));
}
return results;
};
})(this))(el);
}
visited = {};
sortedNames = [];
for (k = 0, len1 = namesWithDependencies.length; k < len1; k++) {
el = namesWithDependencies[k];
(visit = (function(_this) {
return function(name, ancestors) {
var depname, fxModel, l, len2, ref;
fxModel = _this.fxModelMap[name];
if (visited[name] != null) {
return;
}
ancestors.push(name);
visited[name] = true;
ref = fxModel.dependencies;
for (l = 0, len2 = ref.length; l < len2; l++) {
depname = ref[l];
if (indexOf.call(ancestors, depname) >= 0) {
throw new Error('dependency chain is making loop');
}
visit(depname, ancestors.slice());
}
return sortedNames.push(name);
};
})(this))(el, []);
}
return sortedNames;
};
return Fixture;
})();
/**
@class FixtureModel
*/
FixtureModel = (function() {
/**
@constructor
*/
function FixtureModel(fx, name1, setting, dirname1) {
var ref, ref1;
this.fx = fx;
this.name = name1;
if (setting == null) {
setting = {};
}
this.dirname = dirname1;
this.dependencies = (ref = setting.dependencies) != null ? ref : [];
this.data = (ref1 = setting.data) != null ? ref1 : function() {};
}
/**
inserts data to datasource
@method insert
@return {Promise}
*/
FixtureModel.prototype.insert = function() {
var dataNames, factory, insert, modelDataMap, repository, useAnonymousFactory;
modelDataMap = (function() {
switch (typeof this.data) {
case 'string':
return this.readTSV(this.data);
case 'function':
return this.data(this.fx.dataPool);
}
}).call(this);
dataNames = Object.keys(modelDataMap);
if (this.fx.debug) {
console.log("inserting " + dataNames.length + " data into " + this.name);
}
useAnonymousFactory = true;
factory = this.fx.facade.createFactory(this.name, useAnonymousFactory);
repository = this.fx.facade.createRepository(this.name, {
debug: false
});
return (insert = (function(_this) {
return function() {
var data, dataName, model;
if (dataNames.length === 0) {
return Promise.resolve(true);
}
dataName = dataNames.shift();
data = modelDataMap[dataName];
model = factory.createFromObject(data);
return repository.save(model).then(function(savedModel) {
_this.fx.addToDataPool(_this.name, dataName, savedModel);
return insert();
});
};
})(this))();
};
/**
read TSV, returns model data
@method readTSV
*/
FixtureModel.prototype.readTSV = function(filename) {
var data, dataName, i, j, k, len, len1, line, lines, name, names, obj, objs, tsv, value;
objs = {};
lines = fs.readFileSync(this.dirname + '/tsvs/' + filename, 'utf8').split('\n');
tsv = (function() {
var j, len, results;
results = [];
for (j = 0, len = lines.length; j < len; j++) {
line = lines[j];
results.push(line.split('\t'));
}
return results;
})();
names = tsv.shift();
names.shift();
for (j = 0, len = tsv.length; j < len; j++) {
data = tsv[j];
obj = {};
dataName = data.shift();
if (!dataName) {
break;
}
for (i = k = 0, len1 = names.length; k < len1; i = ++k) {
name = names[i];
if (!name) {
break;
}
value = data[i];
if (value.match(/^[0-9]+$/)) {
value = Number(value);
}
obj[name] = value;
}
objs[dataName] = obj;
}
return objs;
};
return FixtureModel;
})();
module.exports = Fixture;