react 0.3.0 → 0.5.1

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.
Files changed (44) hide show
  1. package/.npmignore +2 -1
  2. package/README.md +99 -155
  3. package/doc/advanced.md +166 -0
  4. package/doc/color-def.graffle +938 -0
  5. package/doc/color-def.png +0 -0
  6. package/doc/default-simple.dot +19 -0
  7. package/doc/default-simple.dot.png +0 -0
  8. package/examples/{default1.js → longer-example.js} +0 -0
  9. package/examples/simple.js +45 -0
  10. package/examples/{ast1.js → using-ast-directly.js} +4 -0
  11. package/examples/using-events1.js +79 -0
  12. package/examples/{default-events1.js → using-log-events.js} +5 -14
  13. package/lib/base-task.js +7 -6
  14. package/lib/core.js +33 -14
  15. package/lib/event-collector.js +68 -0
  16. package/lib/event-manager.js +16 -13
  17. package/lib/finalcb-first-task.js +14 -11
  18. package/lib/finalcb-task.js +15 -11
  19. package/lib/id.js +1 -0
  20. package/lib/log-events.js +86 -0
  21. package/{promise-resolve.js → lib/promise-resolve.js} +5 -5
  22. package/lib/task.js +18 -8
  23. package/lib/track-tasks.js +60 -0
  24. package/lib/vcon.js +1 -1
  25. package/package.json +2 -2
  26. package/react.js +33 -1
  27. package/test/ast.test.js +27 -1
  28. package/test/core.test.js +71 -57
  29. package/test/dsl.test.js +2 -2
  30. package/test/module-use.test.js +14 -9
  31. package/test/promise-auto-resolve.test.js +2 -1
  32. package/Jakefile.js +0 -8
  33. package/dsl/chain.js +0 -150
  34. package/dsl/fstr.js +0 -121
  35. package/dsl/pcode.js +0 -175
  36. package/examples/chain-events1.js +0 -34
  37. package/examples/chain1.js +0 -19
  38. package/examples/fstr-events1.js +0 -52
  39. package/examples/fstr1.js +0 -37
  40. package/examples/pcode1.js +0 -22
  41. package/jake-tasks/jake-test.js +0 -64
  42. package/test/dsl/chain.test.js +0 -323
  43. package/test/dsl/fstr.test.js +0 -300
  44. package/test/dsl/pcode.test.js +0 -456
@@ -4,23 +4,23 @@ var sprintf = require('sprintf').sprintf;
4
4
  var util = require('util');
5
5
 
6
6
  var STATUS = require('./status.js');
7
+ var EventManager = require('./event-manager.js');
7
8
 
8
9
  var OUTTASK_A_REQ = 'ast.outTask.a should be an array of string param names';
9
10
 
10
11
  function FinalCbTask(outTaskOptions) {
11
12
  var taskDef = outTaskOptions.taskDef;
12
- var cbFunc = outTaskOptions.cbFunc;
13
- var tasks = outTaskOptions.tasks;
14
- var vCon = outTaskOptions.vCon;
15
- var execOptions = outTaskOptions.execOptions;
16
- var retValue = outTaskOptions.retValue;
17
- if (typeof(cbFunc) !== 'function') throw new Error('callback is not a function');
13
+ if (typeof(outTaskOptions.cbFunc) !== 'function') throw new Error('callback is not a function');
18
14
  var self = this;
19
- Object.keys(taskDef).forEach(function (k) { self[k] = taskDef[k]; });
20
- this.f = cbFunc;
21
- this.tasks = tasks;
22
- this.vCon = vCon;
23
- this.retValue = retValue;
15
+ for (var k in taskDef) {
16
+ if (true) self[k] = taskDef[k]; // if to make jshint happy
17
+ }
18
+ this.f = outTaskOptions.cbFunc;
19
+ this.tasks = outTaskOptions.tasks;
20
+ this.vCon = outTaskOptions.vCon;
21
+ this.retValue = outTaskOptions.retValue;
22
+ this.execOptions = outTaskOptions.execOptions;
23
+ this.env = outTaskOptions.env;
24
24
  }
25
25
 
26
26
  function format_error(errmsg, obj) {
@@ -44,11 +44,15 @@ FinalCbTask.prototype.isReady = function () {
44
44
  FinalCbTask.prototype.exec = function (err) {
45
45
  if (!this.f) return; //must have already been called
46
46
  if (err) {
47
+ this.env.error = err;
48
+ this.env.flowEmitter.emit(EventManager.TYPES.EXEC_FLOW_ERRORED, this.env);
47
49
  this.f.call(null, err); //call the final callback with the first error hit
48
50
  } else { // no error, call with args
49
51
  var vCon = this.vCon;
50
52
  var finalArgs = this.a.map(function (k) { return vCon.getVar(k); });
51
53
  finalArgs.unshift(null); //unshift err=null to front
54
+ this.env.results = finalArgs;
55
+ this.env.flowEmitter.emit(EventManager.TYPES.EXEC_FLOW_COMPLETE, this.env);
52
56
  this.f.apply(null, finalArgs);
53
57
  }
54
58
  this.f = null; // prevent multiple calls
package/lib/id.js CHANGED
@@ -4,6 +4,7 @@ var startingId = 0;
4
4
 
5
5
  function createUniqueId() {
6
6
  startingId += 1;
7
+ if (startingId === Number.MAX_VALUE) startingId = 0; // if hits this start over //TODO need something better?
7
8
  return startingId;
8
9
  }
9
10
 
@@ -0,0 +1,86 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ Log events to console.error
5
+
6
+ @example
7
+ var react = require('react');
8
+ react.logEvents(); // log all task and flow events on all react functions
9
+ react.logEvents('task.*'); // log all task events on all react functions
10
+ react.logEvents(flowFn); // log all task and flow events on flowFn only
11
+ react.logEvents(flowFn, 'flow.*'); // log all flow events on flowFn only
12
+ */
13
+
14
+ var util = require('util'); // TODO replace inspect with something portable to browser
15
+
16
+ var react = require('../'); // require('react');
17
+ react.trackTasks(); // enable task and flow tracking
18
+
19
+ var ALL_FLOW_EVENTS = 'flow.*';
20
+ var ALL_TASK_EVENTS = 'task.*';
21
+ var FLOW_RE = /^flow\./;
22
+
23
+ function flowLog(obj) {
24
+ /*jshint validthis: true */
25
+ var time = new Date();
26
+ time.setTime(obj.time);
27
+ var argsNoCb = obj.args.filter(function (a) { return (typeof(a) !== 'function'); });
28
+ var eventTimeStr = time.toISOString();
29
+ if (this.event === 'flow.complete') {
30
+ var env = obj;
31
+ console.error('%s: %s \tmsecs: %s \n\targs: %s \n\tresults: %s\n',
32
+ this.event, env.name, env.elapsedTime, util.inspect(argsNoCb), util.inspect(env.results));
33
+ } else {
34
+ var name = obj.name;
35
+ var args = obj.args;
36
+ console.error('%s: %s \n\targs: %s\n', this.event, name, util.inspect(argsNoCb));
37
+ }
38
+ }
39
+
40
+ function taskLog(obj) {
41
+ /*jshint validthis: true */
42
+ var time = new Date();
43
+ time.setTime(obj.time);
44
+ var argsNoCb = obj.args.filter(function (a) { return (typeof(a) !== 'function'); });
45
+ var eventTimeStr = time.toISOString();
46
+ if (this.event === 'task.complete') {
47
+ var task = obj;
48
+ console.error('%s: %s:%s \tmsecs: %s \n\targs: %s \n\tresults: %s\n',
49
+ this.event, task.env.name, task.name, task.elapsedTime, util.inspect(argsNoCb), util.inspect(task.results));
50
+ } else {
51
+ var name = obj.name;
52
+ var args = obj.args;
53
+ console.error('%s: %s:%s \n\targs: %s\n', this.event, obj.env.name, obj.name, util.inspect(argsNoCb));
54
+ }
55
+
56
+ }
57
+
58
+ /**
59
+ Log flow and task events for a flowFn or all of react.
60
+ If called multiple times, remove previous listener (if any) before
61
+ adding.
62
+
63
+ @example
64
+ var react = require('react');
65
+ react.logEvents(flowFn, eventWildcard); //log events on flowfn matching wildcard
66
+
67
+ @param flowFn Flow function or global react object
68
+ @param eventWildcard wildcarded event type, if not provided use flow.* and task.*
69
+ */
70
+ function logEvents(flowFn, eventWildcard) {
71
+ if (!flowFn) flowFn = react; // use global
72
+ if (eventWildcard && eventWildcard !== '*') {
73
+ var logFn = (FLOW_RE.test(eventWildcard)) ? flowLog : taskLog;
74
+ flowFn.events.removeListener(eventWildcard, logFn);
75
+ flowFn.events.on(eventWildcard, logFn);
76
+ } else { // none provided, use flow.* and task.*
77
+ //output events as tasks start and complete
78
+ flowFn.events.removeListener(ALL_FLOW_EVENTS, flowLog);
79
+ flowFn.events.on(ALL_FLOW_EVENTS, flowLog);
80
+ flowFn.events.removeListener(ALL_TASK_EVENTS, taskLog);
81
+ flowFn.events.on(ALL_TASK_EVENTS, taskLog);
82
+ }
83
+ }
84
+
85
+ module.exports = react;
86
+ module.exports.logEvents = logEvents;
@@ -10,13 +10,13 @@
10
10
  */
11
11
 
12
12
 
13
- var react = require('./'); // require('react');
13
+ var react = require('../'); // require('react');
14
14
 
15
15
  var PROMISE_SUFFIX = '__promise'; // added to param names that are promises
16
16
 
17
- react.events.on(react.events.TYPES.EXEC_TASKS_PRECREATE, function (taskEnv) {
18
- var vConValues = taskEnv.vCon.values;
19
- var promiseParams = taskEnv.ast.inParams.filter(function (p) {
17
+ react.events.on(react.events.TYPES.EXEC_TASKS_PRECREATE, function (env) {
18
+ var vConValues = env.vCon.values;
19
+ var promiseParams = env.ast.inParams.filter(function (p) {
20
20
  var value = vConValues[p];
21
21
  return (value && typeof(value.then) === 'function');
22
22
  });
@@ -24,7 +24,7 @@ react.events.on(react.events.TYPES.EXEC_TASKS_PRECREATE, function (taskEnv) {
24
24
  var promiseName = p + PROMISE_SUFFIX;
25
25
  vConValues[promiseName] = vConValues[p];
26
26
  vConValues[p] = undefined;
27
- taskEnv.taskDefs.push({
27
+ env.taskDefs.push({
28
28
  type: 'when',
29
29
  a: [promiseName],
30
30
  out: [p]
package/lib/task.js CHANGED
@@ -20,6 +20,9 @@ var TASK_TYPES = {
20
20
  promise: PromiseTask,
21
21
  when: WhenTask
22
22
  };
23
+
24
+ var DEFAULT_TASK_NAME = 'task_%s'; // for unnamed tasks use task_idx, like task_0
25
+
23
26
  function taskTypeKeys() { return Object.keys(TASK_TYPES); }
24
27
 
25
28
  var OUT_TASK_TYPES = {
@@ -127,9 +130,10 @@ function validateLocalFunctions(inParams, taskDefs, locals) {
127
130
  }
128
131
 
129
132
  function fName(fn) {
130
- return (fn) ?
131
- ((fn.name) ? fn.name : fn) : // if defined, try name, otherwise toString()
132
- 'undefined'; // not defined, use undefined
133
+ if (typeof(fn) === 'function') {
134
+ return fn.name;
135
+ }
136
+ return (fn) ? fn : '';
133
137
  }
134
138
 
135
139
  /**
@@ -148,6 +152,7 @@ function nameTasks(tasks) { //name tasks that are not already named, validation
148
152
  tasks.forEach(function (t, idx) {
149
153
  if (!t.name) { //not already named
150
154
  var name = fName(t.f);
155
+ if (!name) name = sprintf(DEFAULT_TASK_NAME, idx);
151
156
  if (!name || namesMap[name]) {
152
157
  name = sprintf('%s_%s', name, idx); //if empty or already used, postfix with _idx
153
158
  }
@@ -163,7 +168,7 @@ function create(taskDef) {
163
168
  return new TaskConstructor(taskDef);
164
169
  }
165
170
 
166
- function createOutTask(taskDef, cbFunc, tasks, vCon, execOptions) {
171
+ function createOutTask(taskDef, cbFunc, tasks, vCon, execOptions, env) {
167
172
  setMissingOutTaskType(taskDef);
168
173
  var outTaskOptions = {
169
174
  taskDef: taskDef,
@@ -171,6 +176,7 @@ function createOutTask(taskDef, cbFunc, tasks, vCon, execOptions) {
171
176
  tasks: tasks,
172
177
  vCon: vCon,
173
178
  execOptions: execOptions,
179
+ env: env,
174
180
  TaskConstructor: OUT_TASK_TYPES[taskDef.type]
175
181
  };
176
182
  EventManager.global.emit(EventManager.TYPES.EXEC_OUTTASK_CREATE, outTaskOptions); // hook
@@ -181,6 +187,9 @@ function createOutTask(taskDef, cbFunc, tasks, vCon, execOptions) {
181
187
  function createErrorHandler(vCon, outTask) {
182
188
  return function handleError(task, err) {
183
189
  task.status = STATUS.ERRORED;
190
+ task.error = err;
191
+ outTask.env.currentTask = task;
192
+ outTask.env.flowEmitter.emit(EventManager.TYPES.EXEC_TASK_ERRORED, task);
184
193
  var errWithMeta = error.augmentError(err, {task: task, vcon: vCon});
185
194
  outTask.exec(errWithMeta); //call the final callback with the first error hit
186
195
  };
@@ -202,7 +211,7 @@ function execTasks(tasksReady, vCon, handleError, contExec) {
202
211
  case everything is fine. If no tasks are running then
203
212
  call handleError since this will never complete.
204
213
  */
205
- function checkIfTasksRunning(vCon, tasks, handleError) {
214
+ function checkIfTasksRunning(vCon, tasks, handleError, env) {
206
215
  var tasksRunning = tasks.filter(function (t) {
207
216
  return (t.status === STATUS.RUNNING || t.status === STATUS.READY);
208
217
  });
@@ -210,13 +219,14 @@ function checkIfTasksRunning(vCon, tasks, handleError) {
210
219
  var remainingTasks = tasks.filter(function (t) { return (!t.status); });
211
220
  var remainingTNames = remainingTasks.map(function (t) { return t.name; });
212
221
  var errMsg = sprintf(NO_TASKS_RUNNING_WONT_COMPLETE, remainingTNames.join(', '));
213
- handleError({}, new Error(errMsg));
222
+ var emptyTask = { env: env };
223
+ handleError(emptyTask, new Error(errMsg));
214
224
  }
215
225
  }
216
226
 
217
- function findReadyAndExec(vCon, tasks, tasksByName, handleError, contExec) {
227
+ function findReadyAndExec(vCon, tasks, tasksByName, handleError, contExec, env) {
218
228
  var tasksReady = findTasksReady(vCon, tasks, tasksByName);
219
- if (!tasksReady.length) checkIfTasksRunning(vCon, tasks, handleError); // no tasks to run, check if ok
229
+ if (!tasksReady.length) checkIfTasksRunning(vCon, tasks, handleError, env); // no tasks to run, check if ok
220
230
  execTasks(tasksReady, vCon, handleError, contExec);
221
231
  }
222
232
 
@@ -0,0 +1,60 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ Track the tasks, start, complete, args, results, elapsed time
5
+ Emits events that can be monitored
6
+
7
+ - track start and complete
8
+ - record args each task was called with
9
+ - record results at completion
10
+ - record start, end, and calc elapsed time
11
+ - emits flow.begin with flowEnv
12
+ - emits task.begin with task
13
+ - emits task.complete with task
14
+ - emits flow complete with flowEnv
15
+ - emits flow errored with flowEnv
16
+
17
+ @example
18
+ var react = require('react');
19
+ react.trackTasks(); // enable task and flow tracking
20
+ */
21
+
22
+
23
+ var react = require('../'); // require('react');
24
+
25
+ react.events.on(react.events.TYPES.EXEC_FLOW_START, function (env){
26
+ env.startTime = Date.now();
27
+ env.flowEmitter.emit(react.events.TYPES.FLOW_BEGIN, env); //fire public ev
28
+ });
29
+
30
+ react.events.on(react.events.TYPES.EXEC_TASK_START, function (task) {
31
+ task.startTime = Date.now();
32
+ task.env.flowEmitter.emit(react.events.TYPES.TASK_BEGIN, task); //fire public ev
33
+ });
34
+
35
+ react.events.on(react.events.TYPES.EXEC_TASK_COMPLETE, function (task) {
36
+ task.endTime = Date.now();
37
+ task.elapsedTime = task.endTime - task.startTime;
38
+ task.env.flowEmitter.emit(react.events.TYPES.TASK_COMPLETE, task); // fire public ev
39
+ });
40
+
41
+ react.events.on(react.events.TYPES.EXEC_TASK_ERRORED, function (task) {
42
+ task.endTime = Date.now();
43
+ task.elapsedTime = task.endTime - task.startTime;
44
+ task.env.flowEmitter.emit(react.events.TYPES.TASK_ERRORED, task); // fire public ev
45
+ });
46
+
47
+ react.events.on(react.events.TYPES.EXEC_FLOW_COMPLETE, function (env) {
48
+ env.endTime = Date.now();
49
+ env.elapsedTime = env.endTime - env.startTime;
50
+ env.flowEmitter.emit(react.events.TYPES.FLOW_COMPLETE, env); //fire public ev
51
+ });
52
+
53
+ react.events.on(react.events.TYPES.EXEC_FLOW_ERRORED, function (env) {
54
+ env.endTime = Date.now();
55
+ env.elapsedTime = env.endTime - env.startTime;
56
+ env.flowEmitter.emit(react.events.TYPES.FLOW_ERRORED, env); //fire public ev
57
+ });
58
+
59
+
60
+ module.exports = react; // return react
package/lib/vcon.js CHANGED
@@ -71,7 +71,7 @@ VContext.create = function (args, inParams, locals, self) {
71
71
  var vContext = new VContext();
72
72
  vContext.values = args.reduce(function (vcon, x, idx) { // create vCon start with input args
73
73
  var param = inParams[idx];
74
- if (param) vcon[param] = x;
74
+ if (param) vcon[param] = (x !== undefined) ? x : null; // upgrade undefined to null
75
75
  return vcon;
76
76
  }, initValues);
77
77
  return vContext;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react",
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.3.0",
3
+ "description": "React is a javascript module implementing a lightweight rules engine 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.5.1",
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" },
package/react.js CHANGED
@@ -2,7 +2,39 @@
2
2
 
3
3
  var core = require('./lib/core.js');
4
4
 
5
+ /**
6
+ If called, load the built-in plugin for log events and invoke
7
+
8
+ @param flowFn [function] if not provided uses global react
9
+ @param eventWildcard [string] pattern to log events for
10
+ */
11
+ function logEvents(flowFn, eventWildcard) {
12
+ var logEventsMod = require('./lib/log-events');
13
+ if (!eventWildcard && typeof(flowFn) === 'string') { // only wildcard provided
14
+ eventWildcard = flowFn;
15
+ flowFn = undefined;
16
+ }
17
+ return logEventsMod.logEvents(flowFn, eventWildcard);
18
+ }
19
+
20
+ /**
21
+ Enable detection of promises and resolution
22
+ */
23
+ function resolvePromises() {
24
+ require('./lib/promise-resolve');
25
+ }
26
+
27
+ /**
28
+ Enable tracking of tasks and flow execution, emitting events and
29
+ tracking start, end, elapsed time
30
+ */
31
+ function trackTasks() {
32
+ require('./lib/track-tasks');
33
+ }
34
+
5
35
  module.exports = require('./lib/dsl.js'); // core + default dsl
6
36
  module.exports.options = core.options; // global react options
7
37
  module.exports.events = core.events; // global react event emitter
8
-
38
+ module.exports.logEvents = logEvents; // enable event logging
39
+ module.exports.resolvePromises = resolvePromises; // enable promise resolution
40
+ module.exports.trackTasks = trackTasks; // enable tracking of tasks
package/test/ast.test.js CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  var test = require('tap').test;
4
4
 
5
- var react = require('../react');
5
+ var react = require('../'); // require('react');
6
+ var EventCollector = require('../lib/event-collector');
6
7
 
7
8
  function load(res, cb) { cb(null, res + '-loaded'); }
8
9
  function prefix(prefstr, str, cb) { cb(null, prefstr + str); }
@@ -38,6 +39,31 @@ test('mixed', function (t) {
38
39
  });
39
40
  });
40
41
 
42
+ test('ast.defined event called when ast is defined', function (t) {
43
+ var fn = react();
44
+ var collector = new EventCollector();
45
+ collector.capture(fn, 'ast.*');
46
+
47
+ var errors = fn.setAndValidateAST({
48
+ inParams: ['res', 'prefstr', 'poststr'],
49
+ tasks: [
50
+ { f: load, a: ['res'], out: ['lres'] },
51
+ { f: upper, a: ['lres'], out: ['ulres'], type: 'ret' },
52
+ { f: prefix, a: ['prefstr', 'ulres'], out: ['plres'] },
53
+ { f: postfix, a: ['plres', 'poststr'], out: ['plresp'] }
54
+ ],
55
+ outTask: { a: ['plresp'] }
56
+ });
57
+
58
+ var events = collector.list();
59
+ t.equal(events.length, 1);
60
+ t.type(events[0].ast, 'object');
61
+ t.ok(events[0].ast.inParams);
62
+ t.ok(events[0].ast.tasks);
63
+ t.ok(events[0].ast.outTask);
64
+ t.end();
65
+ });
66
+
41
67
  test('cb with err', function (t) {
42
68
  t.plan(5);
43
69
 
package/test/core.test.js CHANGED
@@ -3,12 +3,14 @@
3
3
  var test = require('tap').test;
4
4
 
5
5
  var react = require('../react');
6
+ var EventCollector = require('../lib/event-collector'); // require('react/lib/event-collector'); // turn on tracking and get EventCollector
6
7
 
7
8
  function multiply(x, y, cb) { cb(null, x * y); }
8
9
  function add(x, y, cb) { cb(null, x + y); }
9
10
  function badFunc(a, b, cb) { throw new Error('badFuncThrow'); }
10
11
  function badF2(a, b, cb) { cb('my-error'); }
11
12
  function fnRetsSum(a, b) { return a + b; }
13
+ var anonFn = function (a, b) { return a + b; };
12
14
 
13
15
  test('set and validate AST', function (t) {
14
16
  var fn = react();
@@ -41,16 +43,19 @@ test('unnamed tasks will be assigned unique names', function (t) {
41
43
  { f: multiply, a: ['a', 'b'], out: ['c'] },
42
44
  { f: multiply, a: ['a', 'b'], out: ['d'], name: 'multiply' },
43
45
  { f: multiply, a: ['a', 'b'], out: ['e'], name: 'times' },
46
+ { f: anonFn, a: ['a', 'b'], out: ['g'], type: 'ret' },
44
47
  { f: multiply, a: ['a', 'b'], out: ['f'] }
45
48
  ],
46
49
  outTask: { a: ['c'] }
47
50
  });
48
51
  t.deepEqual(errors, [], 'should set and validate as true');
52
+ t.equal(fn.ast.name.slice(0, 'flow_'.length), 'flow_', 'generated flow name should start with flow_');
49
53
  t.deepEqual(fn.ast.tasks, [
50
54
  { f: multiply, a: ['a', 'b'], out: ['c'], type: 'cb', name: 'multiply_0' },
51
55
  { f: multiply, a: ['a', 'b'], out: ['d'], name: 'multiply', type: 'cb' },
52
56
  { f: multiply, a: ['a', 'b'], out: ['e'], name: 'times', type: 'cb' },
53
- { f: multiply, a: ['a', 'b'], out: ['f'], type: 'cb', name: 'multiply_3' }
57
+ { f: anonFn, a: ['a', 'b'], out: ['g'], type: 'ret', name: 'task_3' },
58
+ { f: multiply, a: ['a', 'b'], out: ['f'], type: 'cb', name: 'multiply_4' }
54
59
  ]);
55
60
  t.end();
56
61
  });
@@ -107,19 +112,17 @@ test('multi-step with after as nonarr fn', function (t) {
107
112
  });
108
113
  t.deepEqual(errors, [], 'no validation errors');
109
114
 
110
- var events = [];
111
- function accumEvents(task) {
112
- events.push(task);
113
- }
114
- fn.events.on('task.complete', accumEvents);
115
+ var collector = new EventCollector();
116
+ collector.capture(fn, 'task.complete');
115
117
 
116
118
  fn(2, 3, function (err, c, d) {
117
119
  t.equal(err, null);
118
120
  t.equal(c, 6);
119
121
  t.equal(d, 5);
122
+ var events = collector.list();
120
123
  t.equal(events.length, 2, 'should have seen one task compl events');
121
- t.equal(events[0].name, 'add', 'name matches');
122
- t.equal(events[1].name, 'multiply', 'name matches');
124
+ t.equal(events[0].task.name, 'add', 'name matches');
125
+ t.equal(events[1].task.name, 'multiply', 'name matches');
123
126
  t.end();
124
127
  });
125
128
  });
@@ -137,31 +140,29 @@ test('mixed multi-step with after as nonarr fn w/events', function (t) {
137
140
  });
138
141
  t.deepEqual(errors, [], 'no validation errors');
139
142
 
140
- var events = [];
141
- function accumEvents(task) {
142
- events.push(task);
143
- }
144
- fn.events.on('task.complete', accumEvents);
143
+ var collector = new EventCollector();
144
+ collector.capture(fn, 'task.complete');
145
145
 
146
146
  fn(2, 3, function (err, c, d) {
147
147
  t.equal(err, null);
148
148
  t.equal(c, 6);
149
149
  t.equal(d, 5);
150
+ var events = collector.list();
150
151
  t.equal(events.length, 2, 'should have seen one task compl events');
151
- t.equal(events[0].name, 'fnRetsSum', 'name matches');
152
- t.ok(events[0].id, 'has unique id');
153
- t.ok(events[0].startTime, 'has startTime');
154
- t.ok(events[0].endTime, 'has endTime');
155
- t.ok(events[0].elapsedTime !== undefined, 'has elapsedTime');
156
- t.ok(events[0].args, 'has args');
157
- t.ok(events[0].results, 'has results');
158
- t.equal(events[1].name, 'multiply', 'name matches');
159
- t.ok(events[1].id, 'has unique id');
160
- t.ok(events[1].startTime, 'has startTime');
161
- t.ok(events[1].endTime, 'has endTime');
162
- t.ok(events[1].elapsedTime !== undefined, 'has elapsedTime');
163
- t.ok(events[1].args, 'has args');
164
- t.ok(events[1].results, 'has results');
152
+ t.equal(events[0].task.name, 'fnRetsSum', 'name matches');
153
+ t.ok(events[0].task.id, 'has unique id');
154
+ t.ok(events[0].task.startTime, 'has startTime');
155
+ t.ok(events[0].task.endTime, 'has endTime');
156
+ t.ok(events[0].task.elapsedTime !== undefined, 'has elapsedTime');
157
+ t.ok(events[0].task.args, 'has args');
158
+ t.ok(events[0].task.results, 'has results');
159
+ t.equal(events[1].task.name, 'multiply', 'name matches');
160
+ t.ok(events[1].task.id, 'has unique id');
161
+ t.ok(events[1].task.startTime, 'has startTime');
162
+ t.ok(events[1].task.endTime, 'has endTime');
163
+ t.ok(events[1].task.elapsedTime !== undefined, 'has elapsedTime');
164
+ t.ok(events[1].task.args, 'has args');
165
+ t.ok(events[1].task.results, 'has results');
165
166
  t.end();
166
167
  });
167
168
  });
@@ -434,6 +435,27 @@ test('using "this" in a sync function', function (t) {
434
435
  }]);
435
436
  });
436
437
 
438
+ test('undefined input arguments will be upgraded from undefined to null', function (t) {
439
+ var fn = react();
440
+ function concat(a, b) {
441
+ return '' + a + b;
442
+ }
443
+ var errors = fn.setAndValidateAST({
444
+ inParams: ['a', 'b'],
445
+ tasks: [
446
+ { f: concat, a: ['a', 'b'], out: ['c'], type: 'ret' }
447
+ ],
448
+ outTask: { a: ['c'] }
449
+ });
450
+ t.deepEqual(errors, [], 'no validation errors');
451
+ fn('first', undefined, function (err, c) { // undefined second param, upgrade to null
452
+ t.equal(err, null);
453
+ t.equal(c, 'firstnull');
454
+ t.end();
455
+ });
456
+ });
457
+
458
+
437
459
 
438
460
  // Select first tests
439
461
 
@@ -451,18 +473,16 @@ test('selectFirst with first succeeding', function (t) {
451
473
  });
452
474
  t.deepEqual(errors, [], 'no validation errors');
453
475
 
454
- var events = [];
455
- function accumEvents(task) {
456
- events.push(task);
457
- }
458
- fn.events.on('task.complete', accumEvents);
476
+ var collector = new EventCollector();
477
+ collector.capture(fn, 'task.complete');
459
478
 
460
479
  fn(2, 3, function (err, c) {
461
480
  t.equal(err, null);
462
481
  t.equal(c, 6);
482
+ var events = collector.list();
463
483
  t.equal(events.length, 1, 'should have seen one task compl events');
464
- t.equal(events[0].name, 'multiply', 'name matches');
465
- t.deepEqual(events[0].results, [6], 'results match');
484
+ t.equal(events[0].task.name, 'multiply', 'name matches');
485
+ t.deepEqual(events[0].task.results, [6], 'results match');
466
486
  t.end();
467
487
  });
468
488
  });
@@ -484,18 +504,16 @@ test('selectFirst with third succeeding', function (t) {
484
504
  });
485
505
  t.deepEqual(errors, [], 'no validation errors');
486
506
 
487
- var events = [];
488
- function accumEvents(task) {
489
- events.push(task);
490
- }
491
- fn.events.on('task.complete', accumEvents);
507
+ var collector = new EventCollector();
508
+ collector.capture(fn, 'task.complete');
492
509
 
493
510
  fn(2, 3, function (err, c) {
494
511
  t.equal(err, null);
495
512
  t.equal(c, 5);
513
+ var events = collector.list();
496
514
  t.equal(events.length, 3, 'should have seen three task compl events');
497
- t.equal(events[2].name, 'add', 'name matches');
498
- t.deepEqual(events[2].results, [5], 'results match');
515
+ t.equal(events[2].task.name, 'add', 'name matches');
516
+ t.deepEqual(events[2].task.results, [5], 'results match');
499
517
  t.end();
500
518
  });
501
519
  });
@@ -521,20 +539,18 @@ test('selectFirst forces order with third succeeding', function (t) {
521
539
  });
522
540
  t.deepEqual(errors, [], 'no validation errors');
523
541
 
524
- var events = [];
525
- function accumEvents(task) {
526
- events.push(task);
527
- }
528
- fn.events.on('task.complete', accumEvents);
542
+ var collector = new EventCollector();
543
+ collector.capture(fn, 'task.complete');
529
544
 
530
545
  fn(2, 3, function (err, c) {
531
546
  t.equal(err, null);
532
547
  t.equal(c, 5);
548
+ var events = collector.list();
533
549
  t.equal(events.length, 3, 'should have seen three task compl events');
534
- t.equal(events[0].name, 'noSuccess', 'name matches');
535
- t.equal(events[1].name, 'noSuccessNull', 'name matches');
536
- t.equal(events[2].name, 'add', 'name matches');
537
- t.deepEqual(events[2].results, [5], 'results match');
550
+ t.equal(events[0].task.name, 'noSuccess', 'name matches');
551
+ t.equal(events[1].task.name, 'noSuccessNull', 'name matches');
552
+ t.equal(events[2].task.name, 'add', 'name matches');
553
+ t.deepEqual(events[2].task.results, [5], 'results match');
538
554
  t.end();
539
555
  });
540
556
  });
@@ -560,18 +576,16 @@ test('selectFirst using direct returns', function (t) {
560
576
  });
561
577
  t.deepEqual(errors, [], 'no validation errors');
562
578
 
563
- var events = [];
564
- function accumEvents(task) {
565
- events.push(task);
566
- }
567
- fn.events.on('task.complete', accumEvents);
579
+ var collector = new EventCollector();
580
+ collector.capture(fn, 'task.complete');
568
581
 
569
582
  fn(2, 3, function (err, c) {
570
583
  t.equal(err, null);
571
584
  t.equal(c, 5);
585
+ var events = collector.list();
572
586
  t.equal(events.length, 3, 'should have seen three task compl events');
573
- t.equal(events[2].name, 'addRet', 'name matches');
574
- t.deepEqual(events[2].results, [5], 'results match');
587
+ t.equal(events[2].task.name, 'addRet', 'name matches');
588
+ t.deepEqual(events[2].task.results, [5], 'results match');
575
589
  t.end();
576
590
  });
577
591
  });