UNPKG

9.39 kBJavaScriptView Raw
1'use strict';
2var DomainError, EntityPool, FixtureLoader, Scope, Util, debug,
3 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; };
4
5EntityPool = require('./entity-pool');
6
7DomainError = require('./lib/domain-error');
8
9Util = require('./util');
10
11debug = require('debug')('base-domain:fixture-loader');
12
13
14/**
15Load fixture data (only works in Node.js)
16
17@class FixtureLoader
18@module base-domain
19 */
20
21FixtureLoader = (function() {
22 function FixtureLoader(facade, fixtureDirs) {
23 this.facade = facade;
24 this.fixtureDirs = fixtureDirs != null ? fixtureDirs : [];
25 if (!Array.isArray(this.fixtureDirs)) {
26 this.fixtureDirs = [this.fixtureDirs];
27 }
28 this.entityPool = new EntityPool;
29 this.fixturesByModel = {};
30 }
31
32
33 /**
34 @method load
35 @public
36 @param {Object} [options]
37 @param {Boolean} [options.async] if true, returns Promise.
38 @return {EntityPool|Promise(EntityPool)}
39 */
40
41 FixtureLoader.prototype.load = function(options) {
42 var e, error, ext, file, fixtureDir, fs, fx, j, k, l, len, len1, len2, modelName, modelNames, names, path, ref, ref1, ref2, ref3, ref4, requireFile;
43 if (options == null) {
44 options = {};
45 }
46 ref = this.facade.constructor, fs = ref.fs, requireFile = ref.requireFile;
47 try {
48 modelNames = [];
49 ref1 = this.fixtureDirs;
50 for (j = 0, len = ref1.length; j < len; j++) {
51 fixtureDir = ref1[j];
52 ref2 = fs.readdirSync(fixtureDir + '/data');
53 for (k = 0, len1 = ref2.length; k < len1; k++) {
54 file = ref2[k];
55 ref3 = file.split('.'), modelName = ref3[0], ext = ref3[1];
56 if (ext !== 'coffee' && ext !== 'js' && ext !== 'json') {
57 continue;
58 }
59 path = fixtureDir + '/data/' + file;
60 fx = requireFile(path);
61 fx.path = path;
62 fx.fixtureDir = fixtureDir;
63 this.fixturesByModel[modelName] = fx;
64 modelNames.push(modelName);
65 }
66 }
67 modelNames = this.topoSort(modelNames);
68 names = (ref4 = options.names) != null ? ref4 : modelNames;
69 modelNames = modelNames.filter(function(name) {
70 return indexOf.call(names, name) >= 0;
71 });
72 if (options.async) {
73 return this.saveAsync(modelNames).then((function(_this) {
74 return function() {
75 return _this.entityPool;
76 };
77 })(this));
78 } else {
79 for (l = 0, len2 = modelNames.length; l < len2; l++) {
80 modelName = modelNames[l];
81 this.loadAndSaveModels(modelName);
82 }
83 return this.entityPool;
84 }
85 } catch (error) {
86 e = error;
87 if (options.async) {
88 return Promise.reject(e);
89 } else {
90 throw e;
91 }
92 }
93 };
94
95
96 /**
97 @private
98 */
99
100 FixtureLoader.prototype.saveAsync = function(modelNames) {
101 var modelName;
102 if (!modelNames.length) {
103 return Promise.resolve(true);
104 }
105 modelName = modelNames.shift();
106 return Promise.resolve(this.loadAndSaveModels(modelName)).then((function(_this) {
107 return function() {
108 return _this.saveAsync(modelNames);
109 };
110 })(this));
111 };
112
113
114 /**
115 @private
116 */
117
118 FixtureLoader.prototype.loadAndSaveModels = function(modelName) {
119 var PORTION_SIZE, data, e, error, fx, ids, repo, saveModelsByPortion;
120 fx = this.fixturesByModel[modelName];
121 data = (function() {
122 switch (typeof fx.data) {
123 case 'string':
124 return this.readTSV(fx.fixtureDir, fx.data);
125 case 'function':
126 return fx.data.call(new Scope(this, fx), this.entityPool);
127 case 'object':
128 return fx.data;
129 }
130 }).call(this);
131 try {
132 repo = this.facade.createPreferredRepository(modelName);
133 } catch (error) {
134 e = error;
135 console.error(e.message);
136 return;
137 }
138 if (data == null) {
139 throw new Error("Invalid fixture in model '" + modelName + "'. Check the fixture file: " + fx.path);
140 }
141 ids = Object.keys(data);
142 debug('inserting %s models into %s', ids.length, modelName);
143 PORTION_SIZE = 5;
144 return (saveModelsByPortion = (function(_this) {
145 return function() {
146 var id, idsPortion, obj, results;
147 if (ids.length === 0) {
148 return;
149 }
150 idsPortion = ids.slice(0, PORTION_SIZE);
151 ids = ids.slice(idsPortion.length);
152 results = (function() {
153 var j, len, results1;
154 results1 = [];
155 for (j = 0, len = idsPortion.length; j < len; j++) {
156 id = idsPortion[j];
157 obj = data[id];
158 obj.id = id;
159 results1.push(this.saveModel(repo, obj));
160 }
161 return results1;
162 }).call(_this);
163 if (Util.isPromise(results[0])) {
164 return Promise.all(results).then(function() {
165 return saveModelsByPortion();
166 });
167 } else {
168 return saveModelsByPortion();
169 }
170 };
171 })(this))();
172 };
173
174 FixtureLoader.prototype.saveModel = function(repo, obj) {
175 var result;
176 result = repo.save(obj, {
177 method: 'create',
178 fixtureInsertion: true,
179 include: {
180 entityPool: this.entityPool
181 }
182 });
183 if (Util.isPromise(result)) {
184 return result.then((function(_this) {
185 return function(entity) {
186 return _this.entityPool.set(entity);
187 };
188 })(this));
189 } else {
190 return this.entityPool.set(result);
191 }
192 };
193
194
195 /**
196 topological sort
197
198 @method topoSort
199 @private
200 */
201
202 FixtureLoader.prototype.topoSort = function(names) {
203 var add, el, j, k, len, len1, namesWithDependencies, sortedNames, visit, visited;
204 namesWithDependencies = [];
205 for (j = 0, len = names.length; j < len; j++) {
206 el = names[j];
207 (add = (function(_this) {
208 return function(name) {
209 var depname, fx, k, len1, ref, ref1, results1;
210 if (indexOf.call(namesWithDependencies, name) >= 0) {
211 return;
212 }
213 namesWithDependencies.push(name);
214 fx = _this.fixturesByModel[name];
215 if (fx == null) {
216 throw new DomainError('base-domain:modelNotFound', "model '" + name + "' is not found. It might be written in some 'dependencies' property.");
217 }
218 ref1 = (ref = fx.dependencies) != null ? ref : [];
219 results1 = [];
220 for (k = 0, len1 = ref1.length; k < len1; k++) {
221 depname = ref1[k];
222 results1.push(add(depname));
223 }
224 return results1;
225 };
226 })(this))(el);
227 }
228 visited = {};
229 sortedNames = [];
230 for (k = 0, len1 = namesWithDependencies.length; k < len1; k++) {
231 el = namesWithDependencies[k];
232 (visit = (function(_this) {
233 return function(name, ancestors) {
234 var depname, fx, l, len2, ref, ref1;
235 fx = _this.fixturesByModel[name];
236 if (visited[name] != null) {
237 return;
238 }
239 ancestors.push(name);
240 visited[name] = true;
241 ref1 = (ref = fx.dependencies) != null ? ref : [];
242 for (l = 0, len2 = ref1.length; l < len2; l++) {
243 depname = ref1[l];
244 if (indexOf.call(ancestors, depname) >= 0) {
245 throw new DomainError('base-domain:dependencyLoop', 'dependency chain is making loop');
246 }
247 visit(depname, ancestors.slice());
248 }
249 return sortedNames.push(name);
250 };
251 })(this))(el, []);
252 }
253 return sortedNames;
254 };
255
256
257 /**
258 read TSV, returns model data
259
260 @method readTSV
261 @private
262 */
263
264 FixtureLoader.prototype.readTSV = function(fixtureDir, file) {
265 var data, fs, i, id, j, k, len, len1, line, lines, name, names, obj, objs, tsv, value;
266 fs = this.facade.constructor.fs;
267 objs = {};
268 lines = fs.readFileSync(fixtureDir + '/tsvs/' + file, 'utf8').split('\n');
269 tsv = (function() {
270 var j, len, results1;
271 results1 = [];
272 for (j = 0, len = lines.length; j < len; j++) {
273 line = lines[j];
274 results1.push(line.split('\t'));
275 }
276 return results1;
277 })();
278 names = tsv.shift();
279 names.shift();
280 for (j = 0, len = tsv.length; j < len; j++) {
281 data = tsv[j];
282 obj = {};
283 id = data.shift();
284 obj.id = id;
285 if (!id) {
286 break;
287 }
288 for (i = k = 0, len1 = names.length; k < len1; i = ++k) {
289 name = names[i];
290 if (!name) {
291 break;
292 }
293 value = data[i];
294 if (value.match(/^[0-9]+$/)) {
295 value = Number(value);
296 }
297 obj[name] = value;
298 }
299 objs[obj.id] = obj;
300 }
301 return objs;
302 };
303
304 return FixtureLoader;
305
306})();
307
308
309/**
310'this' property in fixture's data function
311
312this.readTSV('xxx.tsv') is available
313
314 module.exports = {
315 data: function(entityPool) {
316 this.readTSV('model-name.tsv');
317 }
318 };
319
320@class Scope
321@private
322 */
323
324Scope = (function() {
325 function Scope(loader, fx1) {
326 this.loader = loader;
327 this.fx = fx1;
328 }
329
330
331 /**
332 @method readTSV
333 @param {String} filename filename (directory is automatically set)
334 @return {Object} tsv contents
335 */
336
337 Scope.prototype.readTSV = function(filename) {
338 return this.loader.readTSV(this.fx.fixtureDir, filename);
339 };
340
341 return Scope;
342
343})();
344
345module.exports = FixtureLoader;