react 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -242,7 +242,7 @@ fn(2, 3, function (err, m, s) {
242
242
 
243
243
  ## Status
244
244
 
245
- - 2012-01-10 - Create default DSL for react()
245
+ - 2012-01-10 - Create default DSL for react(), create error for missing variables, list remaining tasks when flow won't complete
246
246
  - 2011-12-21 - Refactor from ground up with tests, changes to the interfaces
247
247
  - 2011-10-26 - React is in active development and interface may change frequently in these early stages. Current code is functional but does not perform validation yet. Additional interfaces are planned to make it easy to define flows in a variety of ways. Documentation and examples forthcoming.
248
248
 
package/lib/task.js CHANGED
@@ -25,7 +25,7 @@ function outTaskTypeKeys() { return Object.keys(OUT_TASK_TYPES); }
25
25
 
26
26
  var LOCAL_FN_MISSING = 'function: %s not found in locals or input params - task[%s]';
27
27
  var TASKDEF_IS_OBJECT = 'task must be an object';
28
- var NO_TASKS_RUNNING_WONT_COMPLETE = 'no tasks running, flow will not complete';
28
+ var NO_TASKS_RUNNING_WONT_COMPLETE = 'no tasks running, flow will not complete, remaining tasks: %s';
29
29
  var TASK_TYPE_SHOULD_MATCH = 'task.type should match one of ' +
30
30
  Object.keys(TASK_TYPES).join(', ');
31
31
 
@@ -201,7 +201,12 @@ function checkIfTasksRunning(vCon, tasks, handleError) {
201
201
  var tasksRunning = tasks.filter(function (t) {
202
202
  return (t.status === STATUS.RUNNING || t.status === STATUS.READY);
203
203
  });
204
- if (!tasksRunning.length) handleError({}, new Error(NO_TASKS_RUNNING_WONT_COMPLETE));
204
+ if (!tasksRunning.length) {
205
+ var remainingTasks = tasks.filter(function (t) { return (!t.status); });
206
+ var remainingTNames = remainingTasks.map(function (t) { return t.name; });
207
+ var errMsg = sprintf(NO_TASKS_RUNNING_WONT_COMPLETE, remainingTNames.join(', '));
208
+ handleError({}, new Error(errMsg));
209
+ }
205
210
  }
206
211
 
207
212
  function findReadyAndExec(vCon, tasks, tasksByName, handleError, contExec) {
package/lib/validate.js CHANGED
@@ -11,14 +11,19 @@ var TASKS_ARR = 'ast.tasks must be an array of tasks';
11
11
  var NAMES_UNIQUE = 'ast.tasks that specify name need to be unique, duplicate:';
12
12
  var LOCALS_NOTNULL = 'ast.locals should not be null';
13
13
  var DUP_OUTPUTS = 'multiple tasks output the same param, must be unique. param';
14
+ var MISSING_INPUTS = 'missing or mispelled variable referenced in flow definition: %s';
14
15
 
15
16
  var validateInParams, validateTasks, validateOutTask, validateTaskNamesUnique;
16
- var validateLocals, validateOuputsUnique;
17
+ var validateLocals, validateOuputsUnique, validateNoMissingNames;
17
18
 
18
19
  function format_error(errmsg, obj) {
19
20
  return sprintf('%s - %s', errmsg, util.inspect(obj));
20
21
  }
21
22
 
23
+ function isProp(str) { // true if is a property name (contains a dot)
24
+ return (str.indexOf('.') !== -1);
25
+ }
26
+
22
27
  /**
23
28
  validate the AST return Errors
24
29
  @example
@@ -37,6 +42,7 @@ function validate(ast) {
37
42
  if (errors.length === 0) { // if no errors do additional validation
38
43
  if (ast.outTask.type !== 'finalcbFirst') errors = errors.concat(validateOuputsUnique(ast.tasks));
39
44
  errors = errors.concat(tutil.validateLocalFunctions(ast.inParams, ast.tasks, ast.locals));
45
+ errors = errors.concat(validateNoMissingNames(ast));
40
46
  }
41
47
  return errors;
42
48
  }
@@ -99,4 +105,46 @@ function validateOuputsUnique(taskDefs) {
99
105
  return errors;
100
106
  }
101
107
 
108
+ /**
109
+ validate there are no missing or mispelled param names in any task inputs
110
+ or the final task output
111
+
112
+ @return array of errors, or empty array if none
113
+ */
114
+ function validateNoMissingNames(ast) {
115
+ var errors = [];
116
+ var names = {};
117
+ if (ast.locals) {
118
+ names = Object.keys(ast.locals).reduce(function (accum, k) { // start with locals
119
+ accum[k] = true;
120
+ return accum;
121
+ }, names);
122
+ }
123
+ ast.inParams.reduce(function (accum, p) { // add input params
124
+ accum[p] = true;
125
+ return accum;
126
+ }, names);
127
+ ast.tasks.reduce(function (accum, t) { // add task outputs
128
+ return t.out.reduce(function (innerAccum, p) {
129
+ innerAccum[p] = true;
130
+ return innerAccum;
131
+ }, accum);
132
+ }, names);
133
+
134
+ // now we have all possible provided vars, check task inputs are accounted for
135
+ ast.tasks.reduce(function (accum, t) { // for all tasks
136
+ return t.a.reduce(function (innerAccum, p) { // for all in params, except property
137
+ if (!isProp(p) && !names[p]) innerAccum.push(sprintf(MISSING_INPUTS, p)); // add error if missing
138
+ return innerAccum;
139
+ }, accum);
140
+ }, errors);
141
+
142
+ // now check the final task outputs
143
+ ast.outTask.a.reduce(function (accum, p) { // for final task out params
144
+ if (!isProp(p) && !names[p]) accum.push(sprintf(MISSING_INPUTS, p)); // add error if missing
145
+ return accum;
146
+ }, errors);
147
+ return errors;
148
+ }
149
+
102
150
  module.exports = validate;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react",
3
3
  "description": "React is a javascript module to make it easier to work with asynchronous code, by reducing boilerplate code and improving error and exception handling while allowing variable and task dependencies when defining flow.",
4
- "version": "0.2.2",
4
+ "version": "0.2.3",
5
5
  "author": "Jeff Barczewski <jeff.barczewski@gmail.com>",
6
6
  "repository": { "type": "git", "url": "http://github.com/jeffbski/react.git" },
7
7
  "bugs" : { "url": "http://github.com/jeffbski/react/issues" },
@@ -63,10 +63,11 @@ test('in triple first param -> inParams["foo", "bar", "baz"]', function (t) {
63
63
 
64
64
  test('single task, single out params', function (t) {
65
65
  var fn = chainDefine()
66
+ .in('a', 'b')
66
67
  .out('c')
67
68
  .async(falpha).in('a', 'b').out('c')
68
69
  .end();
69
- t.deepEqual(fn.ast.inParams, []);
70
+ t.deepEqual(fn.ast.inParams, ['a', 'b']);
70
71
  t.deepEqual(fn.ast.tasks, [
71
72
  { f: falpha, type: 'cb', a: ['a', 'b'], out: ['c'], name: 'falpha' }
72
73
  ]);
@@ -77,11 +78,11 @@ test('single task, single out params', function (t) {
77
78
  test('single task, err and out params', function (t) {
78
79
  var fn = chainDefine()
79
80
  .out('err', 'c')
80
- .async(falpha).in('a', 'b').out('err', 'c')
81
+ .async(falpha).in().out('err', 'c')
81
82
  .end();
82
83
  t.deepEqual(fn.ast.inParams, []);
83
84
  t.deepEqual(fn.ast.tasks, [
84
- { f: falpha, type: 'cb', a: ['a', 'b'], out: ['c'], name: 'falpha' }
85
+ { f: falpha, type: 'cb', a: [], out: ['c'], name: 'falpha' }
85
86
  ]);
86
87
  t.deepEqual(fn.ast.outTask, { a: ['c'], type: 'finalcb' });
87
88
  t.end();
@@ -90,11 +91,11 @@ test('single task, err and out params', function (t) {
90
91
  test('single task, ERR and out params', function (t) {
91
92
  var fn = chainDefine()
92
93
  .out('ERR', 'c')
93
- .async(falpha).in('a', 'b').out('ERR', 'c')
94
+ .async(falpha).in().out('ERR', 'c')
94
95
  .end();
95
96
  t.deepEqual(fn.ast.inParams, []);
96
97
  t.deepEqual(fn.ast.tasks, [
97
- { f: falpha, type: 'cb', a: ['a', 'b'], out: ['c'], name: 'falpha' }
98
+ { f: falpha, type: 'cb', a: [], out: ['c'], name: 'falpha' }
98
99
  ]);
99
100
  t.deepEqual(fn.ast.outTask, { a: ['c'], type: 'finalcb' });
100
101
  t.end();
package/test/core.test.js CHANGED
@@ -198,14 +198,15 @@ test('error when cant complete', function (t) {
198
198
  inParams: ['a', 'b', 'c'],
199
199
  tasks: [
200
200
  { f: multiply, a: ['a', 'b'], out: ['c.mult'] },
201
- { f: fnRetsSum, a: ['c.bad', 'b'], out: ['c.sum'], type: 'ret' }
201
+ { f: fnRetsSum, a: ['c.bad', 'b'], out: ['c.sum'], type: 'ret' },
202
+ { f: add, a: ['c.sum', 'a'], out: ['d']}
202
203
  ],
203
- outTask: { a: ['c.mult', 'c.sum', 'c'] }
204
+ outTask: { a: ['c.mult', 'c.sum', 'd'] }
204
205
  });
205
206
  t.deepEqual(errors, [], 'no validation errors');
206
207
 
207
- fn(2, 3, { foo: 1 }, function (err, cmult, csum, c) {
208
- t.equal(err.message, 'no tasks running, flow will not complete');
208
+ fn(2, 3, { foo: 1 }, function (err, cmult, csum, d) {
209
+ t.equal(err.message, 'no tasks running, flow will not complete, remaining tasks: fnRetsSum, add');
209
210
  t.end();
210
211
  });
211
212
  });
package/test/dsl.test.js CHANGED
@@ -55,11 +55,11 @@ test('triple first string -> inParams["foo", "bar", "baz"], empty tasks, outTask
55
55
 
56
56
  test('single task, single out params', function (t) {
57
57
  var r = react('myName', 'cb -> err, c',
58
- falpha, 'a, b, cb -> err, c'
58
+ falpha, 'cb -> err, c'
59
59
  );
60
60
  t.deepEqual(r.ast.inParams, []);
61
61
  t.deepEqual(r.ast.tasks, [
62
- { f: falpha, a: ['a', 'b'], out: ['c'], type: 'cb', name: 'falpha'}
62
+ { f: falpha, a: [], out: ['c'], type: 'cb', name: 'falpha'}
63
63
  ]);
64
64
  t.deepEqual(r.ast.outTask, { a: ['c'], type: 'finalcb' });
65
65
  t.end();
@@ -67,11 +67,11 @@ test('single task, single out params', function (t) {
67
67
 
68
68
  test('single task, err and out params', function (t) {
69
69
  var r = react('myName', 'cb -> err, c',
70
- falpha, 'a, b, cb -> err, c'
70
+ falpha, 'cb -> err, c'
71
71
  );
72
72
  t.deepEqual(r.ast.inParams, []);
73
73
  t.deepEqual(r.ast.tasks, [
74
- { f: falpha, a: ['a', 'b'], out: ['c'], type: 'cb', name: 'falpha'}
74
+ { f: falpha, a: [], out: ['c'], type: 'cb', name: 'falpha'}
75
75
  ]);
76
76
  t.deepEqual(r.ast.outTask, { a: ['c'], type: 'finalcb' });
77
77
  t.end();
@@ -79,11 +79,11 @@ test('single task, err and out params', function (t) {
79
79
 
80
80
  test('single task, ERR and out params', function (t) {
81
81
  var r = react('myName', 'cb -> ERR, c',
82
- falpha, 'a, b, cb -> ERR, c'
82
+ falpha, 'cb -> ERR, c'
83
83
  );
84
84
  t.deepEqual(r.ast.inParams, []);
85
85
  t.deepEqual(r.ast.tasks, [
86
- { f: falpha, a: ['a', 'b'], out: ['c'], type: 'cb', name: 'falpha'}
86
+ { f: falpha, a: [], out: ['c'], type: 'cb', name: 'falpha'}
87
87
  ]);
88
88
  t.deepEqual(r.ast.outTask, { a: ['c'], type: 'finalcb' });
89
89
  t.end();
package/test/fstr.test.js CHANGED
@@ -48,10 +48,10 @@ test('triple first string -> inParams["foo", "bar", "baz"], empty tasks, outTask
48
48
  });
49
49
 
50
50
  test('single task, single out params', function (t) {
51
- var r = fstrDefine('', [
51
+ var r = fstrDefine('a, b', [
52
52
  falpha, 'a, b -> err, c'
53
53
  ], 'c');
54
- t.deepEqual(r.ast.inParams, []);
54
+ t.deepEqual(r.ast.inParams, ['a', 'b']);
55
55
  t.deepEqual(r.ast.tasks, [
56
56
  { f: falpha, a: ['a', 'b'], out: ['c'], type: 'cb', name: 'falpha'}
57
57
  ]);
@@ -60,10 +60,10 @@ test('single task, single out params', function (t) {
60
60
  });
61
61
 
62
62
  test('single task, err and out params', function (t) {
63
- var r = fstrDefine('', [
63
+ var r = fstrDefine('a, b', [
64
64
  falpha, 'a, b -> err, c'
65
65
  ], 'err, c');
66
- t.deepEqual(r.ast.inParams, []);
66
+ t.deepEqual(r.ast.inParams, ['a', 'b']);
67
67
  t.deepEqual(r.ast.tasks, [
68
68
  { f: falpha, a: ['a', 'b'], out: ['c'], type: 'cb', name: 'falpha'}
69
69
  ]);
@@ -72,10 +72,10 @@ test('single task, err and out params', function (t) {
72
72
  });
73
73
 
74
74
  test('single task, ERR and out params', function (t) {
75
- var r = fstrDefine('', [
75
+ var r = fstrDefine('a, b', [
76
76
  falpha, 'a, b -> ERR, c'
77
77
  ], 'ERR, c');
78
- t.deepEqual(r.ast.inParams, []);
78
+ t.deepEqual(r.ast.inParams, ['a', 'b']);
79
79
  t.deepEqual(r.ast.tasks, [
80
80
  { f: falpha, a: ['a', 'b'], out: ['c'], type: 'cb', name: 'falpha'}
81
81
  ]);
@@ -50,11 +50,11 @@ test('triple first string -> inParams["foo", "bar", "baz"], empty tasks, outTask
50
50
 
51
51
  test('single task, single out params', function (t) {
52
52
  var locals = { falpha: falpha };
53
- var r = pcode('', [
53
+ var r = pcode('a, b', [
54
54
  'c := falpha(a, b)',
55
55
  'cb(err, c)'
56
56
  ], locals);
57
- t.deepEqual(r.ast.inParams, []);
57
+ t.deepEqual(r.ast.inParams, ['a', 'b']);
58
58
  t.deepEqual(r.ast.tasks, [
59
59
  { f: 'falpha', a: ['a', 'b'], out: ['c'], type: 'cb', name: 'falpha'}
60
60
  ]);
@@ -64,11 +64,11 @@ test('single task, single out params', function (t) {
64
64
 
65
65
  test('single task, err and out params', function (t) {
66
66
  var locals = { falpha: falpha };
67
- var r = pcode('', [
67
+ var r = pcode('a, b', [
68
68
  'c := falpha(a, b)',
69
69
  'cb(err, c)'
70
70
  ], locals);
71
- t.deepEqual(r.ast.inParams, []);
71
+ t.deepEqual(r.ast.inParams, ['a', 'b']);
72
72
  t.deepEqual(r.ast.tasks, [
73
73
  { f: 'falpha', a: ['a', 'b'], out: ['c'], type: 'cb', name: 'falpha'}
74
74
  ]);
@@ -210,9 +210,57 @@ test('multiple tasks output the same param, must be unique', function (t) {
210
210
  { f: foo, a: [], out: ['baz', 'c'] },
211
211
  { f: bar, a: [], out: ['c'] }
212
212
  ],
213
- outTask: { a: ['bar'] }
213
+ outTask: { a: ['baz'] }
214
214
  };
215
215
  var msg = 'multiple tasks output the same param, must be unique. param: c';
216
216
  t.deepEqual(validate(ast), [msg]);
217
217
  t.end();
218
+ });
219
+
220
+ test('missing or mispelled input variable', function (t) {
221
+ var ast = {
222
+ inParams: [],
223
+ tasks: [
224
+ { f: foo, a: [], out: [] },
225
+ { f: bar, a: ['abc'], out: [] }
226
+ ],
227
+ outTask: { a: [] }
228
+ };
229
+ var msg = 'missing or mispelled variable referenced in flow definition: abc';
230
+ t.deepEqual(validate(ast), [msg]);
231
+ t.end();
232
+ });
233
+
234
+ test('missing or mispelled input variables', function (t) {
235
+ var ast = {
236
+ inParams: ['aaa', 'bbb'],
237
+ tasks: [
238
+ { f: foo, a: ['aaa', 'cat'], out: ['ccc'] },
239
+ { f: bar, a: ['abc', 'bbb', 'ccc'], out: [] }
240
+ ],
241
+ outTask: { a: [] }
242
+ };
243
+ var messages = [
244
+ 'missing or mispelled variable referenced in flow definition: cat',
245
+ 'missing or mispelled variable referenced in flow definition: abc'
246
+ ];
247
+ t.deepEqual(validate(ast), messages);
248
+ t.end();
249
+ });
250
+
251
+ test('missing or mispelled final output variables', function (t) {
252
+ var ast = {
253
+ inParams: ['aaa'],
254
+ tasks: [
255
+ { f: foo, a: ['aaa'], out: ['bbb'] },
256
+ { f: bar, a: ['bbb'], out: ['ccc'] }
257
+ ],
258
+ outTask: { a: ['ccc', 'ddd', 'eee'] }
259
+ };
260
+ var messages = [
261
+ 'missing or mispelled variable referenced in flow definition: ddd',
262
+ 'missing or mispelled variable referenced in flow definition: eee'
263
+ ];
264
+ t.deepEqual(validate(ast), messages);
265
+ t.end();
218
266
  });