1 | 'use strict';
|
2 | var path = require('path')
|
3 | , basename = path.basename(__filename, '.js')
|
4 | , assert = require('assert')
|
5 | , once = require('once')
|
6 | , Promise = typeof global.Promise == 'function' ? global.Promise : require('es6-promise').Promise
|
7 | ;
|
8 |
|
9 |
|
10 | function parallel(tasks, fn) {
|
11 | var errors = [], counter = 0, limit = Object.keys(tasks).length, results = {};
|
12 | if (limit === 0) {
|
13 | fn({}, []);
|
14 | }
|
15 | else {
|
16 | Object.keys(tasks).forEach(function(key) {
|
17 | tasks[key](function(out) {
|
18 | if (counter < limit) {
|
19 | if (out instanceof Error) {
|
20 | errors.push(key);
|
21 | }
|
22 | results[key] = out;
|
23 | counter++;
|
24 | if(counter === limit) {
|
25 | fn(results, errors);
|
26 | }
|
27 | }
|
28 | }
|
29 | );
|
30 | }
|
31 | );
|
32 | }
|
33 | }
|
34 |
|
35 | function Datamodel() {
|
36 |
|
37 | var self = this;
|
38 | self._declared = {};
|
39 | self._appended = {};
|
40 | self._prepended = {};
|
41 | self._appended = {};
|
42 | self._completed = {};
|
43 | self._transformer = null;
|
44 | }
|
45 |
|
46 | Datamodel.prototype.declare = function (key, fn)
|
47 | {
|
48 | assert.equal(typeof key, 'string');
|
49 | assert.equal(typeof fn, 'function');
|
50 | var self = this;
|
51 | self._declared[key] = fn;
|
52 | return self;
|
53 | }
|
54 |
|
55 | Datamodel.prototype.prepend = function (key, fn)
|
56 | {
|
57 | assert.equal(typeof key, 'string');
|
58 | assert.equal(typeof fn, 'function');
|
59 | var self = this;
|
60 | self._prepended[key] = fn;
|
61 | return self;
|
62 | }
|
63 |
|
64 | Datamodel.prototype.append = function (key, fn)
|
65 | {
|
66 | assert.equal(typeof key, 'string');
|
67 | assert.equal(typeof fn, 'function');
|
68 | var self = this;
|
69 | self._appended[key] = fn;
|
70 | return self;
|
71 | }
|
72 |
|
73 | Datamodel.prototype.complete = function (key, fn)
|
74 | {
|
75 | assert.equal(typeof key, 'string');
|
76 | assert.equal(typeof fn, 'function');
|
77 | var self = this;
|
78 | self._completed[key] = fn;
|
79 | return self;
|
80 | }
|
81 |
|
82 | Datamodel.prototype.transform = function (fn)
|
83 | {
|
84 | assert.equal(typeof fn, 'function');
|
85 | var self = this;
|
86 | self._transformer = fn;
|
87 | return self;
|
88 | }
|
89 |
|
90 | Datamodel.prototype.send = function (fn)
|
91 | {
|
92 | assert.equal(typeof fn, 'function');
|
93 | var self = this;
|
94 | self._sender = fn;
|
95 | return self;
|
96 | }
|
97 |
|
98 | var apply = function (self, input, output, fn)
|
99 | {
|
100 | var results = {}, errors = [];
|
101 |
|
102 | function mapper(ino, v) {
|
103 | var ouo = {};
|
104 | Object.keys(ino).forEach(function(k) {
|
105 | ouo[k] = function (callback) {
|
106 | var cb = once(callback);
|
107 | try {
|
108 | ino[k].call(results, input, cb);
|
109 | }
|
110 | catch (e) {
|
111 | cb(e)
|
112 | }
|
113 | }
|
114 | });
|
115 | return ouo;
|
116 | }
|
117 | parallel(mapper(self._declared, 'declare'), function(res1, err1) {
|
118 | Object.keys(res1).forEach(function(key) {
|
119 | results[key] = res1[key];
|
120 | });
|
121 | errors = errors.concat(err1);
|
122 | parallel(mapper(self._prepended, 'prepended'), function(res2a, err2a) {
|
123 | Object.keys(res2a).forEach(function(key) {
|
124 | results[key] = res2a[key];
|
125 | });
|
126 | errors = errors.concat(err2a)
|
127 | parallel(mapper(self._appended, 'appended'), function(res2b, err2b) {
|
128 | Object.keys(res2b).forEach(function(key) {
|
129 | results[key] = res2b[key];
|
130 | });
|
131 | errors = errors.concat(err2b)
|
132 | parallel(mapper(self._completed, 'complete'), function(res3, err3) {
|
133 | Object.keys(res3).forEach(function(key) {
|
134 | results[key] = res3[key];
|
135 | });
|
136 | errors = errors.concat(err3).filter(function(e) {
|
137 | return (results[e] instanceof Error);
|
138 | });
|
139 | var err = null;
|
140 | if (errors.length > 0) {
|
141 | err = results[errors[0]];
|
142 | err.message = "[Datamodel." + errors[0] + "] " + err.message;
|
143 | }
|
144 | if (typeof self._transformer === 'function') {
|
145 | self._transformer.call(results, input, function(newresults) {
|
146 | results = newresults;
|
147 | if (output && typeof self._sender === 'function') {
|
148 | self._sender.call(results, output, fn);
|
149 | }
|
150 | else {
|
151 | fn(err, results);
|
152 | }
|
153 | });
|
154 | }
|
155 | else {
|
156 | if (output && typeof self._sender === 'function') {
|
157 | self._sender.call(results, output, fn);
|
158 | }
|
159 | else {
|
160 | fn(err, results);
|
161 | }
|
162 | }
|
163 | });
|
164 | });
|
165 | });
|
166 | });
|
167 | }
|
168 | Datamodel.prototype.apply = function (input, output, fn)
|
169 | {
|
170 | var self = this;
|
171 | if (typeof input === 'function' && output === undefined && fn === undefined) {
|
172 | fn = input;
|
173 | input = {};
|
174 | output = false;
|
175 | }
|
176 | else if (typeof input !== 'function' && typeof output === 'function' && fn === undefined) {
|
177 | fn = output;
|
178 | output = false;
|
179 | }
|
180 |
|
181 | if(typeof fn == 'function') {
|
182 | return apply(self, input, output, fn);
|
183 | }
|
184 | return new Promise(function(resolve, reject) {
|
185 | apply(self, input, output, function(e, r) {
|
186 | if (e) {
|
187 | reject(e);
|
188 | }
|
189 | else {
|
190 | resolve(r);
|
191 | }
|
192 | });
|
193 | });
|
194 | }
|
195 | Datamodel.prototype.attach = function (module) {
|
196 | var self = this;
|
197 | module.exports = self.takeout();
|
198 | return self;
|
199 | };
|
200 |
|
201 | Datamodel.prototype.takeout = function () {
|
202 | var self = this;
|
203 | return function(input, output, fn) {
|
204 | return self.apply(input, output, fn);
|
205 | }
|
206 | };
|
207 |
|
208 |
|
209 |
|
210 | module.exports = function (models) {
|
211 | var model = new Datamodel();
|
212 |
|
213 | if (Array.isArray(models)) {
|
214 | models.forEach(function(func, i) {
|
215 | if (typeof func === 'function') {
|
216 | func(model);
|
217 | }
|
218 | });
|
219 | }
|
220 |
|
221 | return model;
|
222 | }
|