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
Binary file
@@ -0,0 +1,19 @@
1
+ digraph simple {
2
+ inputs [shape=box];
3
+ loadFoo [shape=box];
4
+ loadBar [shape=box];
5
+ render [shape=box];
6
+ outputs[shape=box];
7
+ inputs -> fooPath;
8
+ inputs -> barPath;
9
+ inputs -> barP2;
10
+ fooPath -> loadFoo;
11
+ loadFoo -> foo;
12
+ barPath -> loadBar;
13
+ loadBar -> bar;
14
+ barP2 -> loadBar;
15
+ foo -> render;
16
+ bar -> render;
17
+ render -> renderedOut;
18
+ renderedOut -> outputs;
19
+ }
Binary file
File without changes
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ Simple example showing flow definition of two async functions feeding a
5
+ synchronous function.
6
+
7
+ First two async functions inputs are satisfied by the flow inputs, so
8
+ they will both run immediately in parallel.
9
+
10
+ The last function waits for the outputs of the previous ones, then
11
+ executes synchronously.
12
+
13
+ Finally the flow calls the callback with the output values once all
14
+ the tasks have completed.
15
+ */
16
+
17
+ var react = require('../'); // require('react');
18
+
19
+ function loadFoo(fooPath, cb) {
20
+ setTimeout(function () {
21
+ cb(null, [fooPath, 'data'].join(':'));
22
+ }, 10);
23
+ }
24
+
25
+ function loadBar(barPath, barP2, cb) {
26
+ setTimeout(function () {
27
+ cb(null, [barPath, barP2, 'data'].join(':'));
28
+ }, 10);
29
+ }
30
+
31
+ function render(foo, bar) {
32
+ return ['<html>', foo, '/', bar, '</html>'].join('');
33
+ }
34
+
35
+
36
+ var fn = react('loadRender', 'fooPath, barPath, barP2, cb -> err, renderedOut',
37
+ loadFoo, 'fooPath, cb -> err, foo',
38
+ loadBar, 'barPath, barP2, cb -> err, bar',
39
+ render, 'foo, bar -> renderedOut'
40
+ );
41
+
42
+
43
+ fn('foo.txt', 'bar.txt', 'BBB', function (err, renderedOut) {
44
+ console.error('results:', renderedOut);
45
+ });
@@ -1,5 +1,9 @@
1
1
  'use strict';
2
2
 
3
+ /**
4
+ Advanced example using the AST directly which most users will not do.
5
+ For general use, see other examples like simple.js
6
+ */
3
7
  var react = require('../'); // require('react');
4
8
 
5
9
  function load(res, cb) { setTimeout(cb, 100, null, res + '-loaded'); }
@@ -0,0 +1,79 @@
1
+ 'use strict';
2
+ /*jshint white: false */
3
+
4
+ /**
5
+ Default DSL, showing use of events
6
+ */
7
+
8
+ var react = require('../'); // require('react');
9
+ require('../lib/track-tasks'); // require('react/lib/track-tasks'); // turn on tracking
10
+
11
+ //output events as tasks start and complete
12
+ react.events.on('flow.*', function (obj) {
13
+ /*jshint validthis: true */
14
+ var time = new Date();
15
+ time.setTime(obj.time);
16
+ var argsNoCb = obj.args.filter(function (a) { return (typeof(a) !== 'function'); });
17
+ var eventTimeStr = time.toISOString();
18
+ if (this.event === 'flow.complete') {
19
+ var env = obj;
20
+ console.error('%s: %s \tmsecs:(%s) \n\targs:(%s) \n\tresults:(%s)\n',
21
+ this.event, env.name, env.elapsedTime, argsNoCb, env.results);
22
+ } else {
23
+ var name = obj.name;
24
+ var args = obj.args;
25
+ console.error('%s: %s \n\targs:(%s)\n', this.event, name, argsNoCb);
26
+ }
27
+ });
28
+
29
+ react.events.on('task.*', function (obj) {
30
+ /*jshint validthis: true */
31
+ var time = new Date();
32
+ time.setTime(obj.time);
33
+ var argsNoCb = obj.args.filter(function (a) { return (typeof(a) !== 'function'); });
34
+ var eventTimeStr = time.toISOString();
35
+ if (this.event === 'task.complete') {
36
+ var task = obj;
37
+ console.error('%s: %s \tmsecs:(%s) \n\targs:(%s) \n\tresults:(%s)\n',
38
+ this.event, task.name, task.elapsedTime, argsNoCb, task.results);
39
+ } else {
40
+ var name = obj.name;
41
+ var args = obj.args;
42
+ console.error('%s: %s \n\targs:(%s)\n', this.event, name, argsNoCb);
43
+ }
44
+ });
45
+
46
+
47
+
48
+ function loadUser(uid, cb){ setTimeout(cb, 100, null, "User"+uid); }
49
+ function loadFile(filename, cb){ setTimeout(cb, 100, null, 'Filedata'+filename); }
50
+ function markdown(filedata) { return 'html'+filedata; }
51
+ function prepareDirectory(outDirname, cb){ setTimeout(cb, 200, null, 'dircreated-'+outDirname); }
52
+ function writeOutput(html, user, cb){ setTimeout(cb, 300, null, html+'_bytesWritten'); }
53
+ function loadEmailTemplate(cb) { setTimeout(cb, 50, null, 'emailmd'); }
54
+ function customizeEmail(user, emailHtml) { return 'cust-'+user+emailHtml; }
55
+ function deliverEmail(custEmailHtml, cb) { setTimeout(cb, 100, null, 'delivered-'+custEmailHtml); }
56
+
57
+ function useHtml(err, html, user, bytesWritten) {
58
+ if (err) {
59
+ console.log('***Error: %s', err);
60
+ return;
61
+ }
62
+ console.log('final result: %s, user: %s, written:%s', html, user, bytesWritten);
63
+ }
64
+
65
+ var loadAndSave = react('loadAndSave', 'filename, uid, outDirname, cb -> err, html, user, bytesWritten', // name, in/out params
66
+ loadUser, 'uid, cb -> err, user', // calling async fn loadUser with uid, callback is called with err and user
67
+ loadFile, 'filename, cb -> err, filedata',
68
+ markdown, 'filedata -> html', // using a sync function
69
+ prepareDirectory, 'outDirname, cb -> err, dircreated',
70
+ writeOutput, 'html, user, cb -> err, bytesWritten', { after: prepareDirectory }, // only after prepareDirectory done
71
+ loadEmailTemplate, 'cb -> err, emailmd',
72
+ markdown, 'emailmd -> emailHtml', // using a sync function
73
+ customizeEmail, 'user, emailHtml -> custEmailHtml', // sync fn
74
+ deliverEmail, 'custEmailHtml, cb -> err, deliveredEmail', { after: writeOutput } // only after writeOutput is done
75
+ );
76
+
77
+ loadAndSave('file.md', 100, '/tmp/foo', useHtml); // executing the flow
78
+
79
+
@@ -1,22 +1,13 @@
1
1
  'use strict';
2
2
  /*jshint white: false */
3
3
 
4
+ /**
5
+ Default DSL, showing use of events
6
+ */
7
+
4
8
  var react = require('../'); // require('react');
9
+ react.logEvents(); // turn on logging of all flow and task events for all react functions
5
10
 
6
- //output events as tasks start and complete
7
- react.events.on('task.*', function (obj) {
8
- var time = new Date();
9
- time.setTime(obj.time);
10
- var eventTimeStr = time.toISOString();
11
- var argsNoCb = obj.args.filter(function (a) { return (typeof(a) !== 'function'); });
12
- if (obj.event === 'task.complete') {
13
- console.error('%s: %s \tmsecs:(%s) \n\targs:(%s) \n\tresults:(%s)\n',
14
- obj.event, obj.name, obj.elapsedTime, argsNoCb, obj.results);
15
- } else {
16
- console.error('%s: %s \n\targs:(%s)\n', obj.event, obj.name, argsNoCb);
17
- }
18
- });
19
-
20
11
 
21
12
  function loadUser(uid, cb){ setTimeout(cb, 100, null, "User"+uid); }
22
13
  function loadFile(filename, cb){ setTimeout(cb, 100, null, 'Filedata'+filename); }
package/lib/base-task.js CHANGED
@@ -19,17 +19,18 @@ BaseTask.prototype.isComplete = function () {
19
19
  };
20
20
 
21
21
  BaseTask.prototype.start = function (args) { // mark task as started with args and note time
22
+ /*jshint validthis: true */
22
23
  this.args = args;
23
- this.startTime = Date.now();
24
- if (this.flowEmitter) this.flowEmitter.emitObject(EventManager.TYPES.TASK_BEGIN, this);
24
+ this.env.currentTask = this;
25
+ this.env.flowEmitter.emit(EventManager.TYPES.EXEC_TASK_START, this);
25
26
  };
26
27
 
27
28
  BaseTask.prototype.complete = function (args) { //args that were used are available
28
- this.results = args; // save the results
29
- this.endTime = Date.now();
30
- this.elapsedTime = this.endTime - this.startTime;
31
- if (this.flowEmitter) this.flowEmitter.emitObject(EventManager.TYPES.TASK_COMPLETE, this);
29
+ /*jshint validthis: true */
32
30
  this.status = STATUS.COMPLETE;
31
+ this.results = args;
32
+ this.env.currentTask = this;
33
+ this.env.flowEmitter.emit(EventManager.TYPES.EXEC_TASK_COMPLETE, this);
33
34
  };
34
35
 
35
36
  BaseTask.prototype.functionExists = function (vCon) {
package/lib/core.js CHANGED
@@ -10,6 +10,7 @@ var VContext = require('./vcon.js');
10
10
  var EventManager = require('./event-manager.js');
11
11
  var inputParser = require('./input-parser.js');
12
12
  var idGenerator = require('./id.js');
13
+ var sprintf = require('sprintf').sprintf;
13
14
 
14
15
  var reactOptions = {
15
16
  stackTraceLimitMin: 30
@@ -27,6 +28,13 @@ function mergeOptions(parsedOptions) {
27
28
  }, parsedOptions);
28
29
  }
29
30
 
31
+ /**
32
+ generate a flow name when one is not provided
33
+ */
34
+ function generateFlowName() {
35
+ return sprintf('flow_%s', idGenerator.createUniqueId());
36
+ }
37
+
30
38
  /**
31
39
  Creates react function which the AST can be manipulated and then
32
40
  is ready to be executed. Can be used directly or a DSL can wrap this
@@ -64,44 +72,55 @@ function reactFactory() {
64
72
  function setAndValidateAST(newAST) { //set AST then validate, ret error[]
65
73
  Object.keys(newAST).forEach(function (k) { ast[k] = newAST[k]; }); // copy all properties
66
74
  var errors = validate(ast);
67
- if (!errors.length) tskutil.nameTasks(ast.tasks); //run this so names can be checked in ast
75
+ if (!errors.length) {
76
+ if (!ast.name) ast.name = generateFlowName();
77
+ tskutil.nameTasks(ast.tasks); //run this so names can be checked in ast
78
+ }
68
79
  if (Object.freeze) { //lets freeze the AST so plugin writers don't accidentally manip the ast
69
80
  Object.keys(newAST).forEach(function (k) {
70
81
  if (typeof(newAST[k]) === 'object') Object.freeze(newAST[k]);
71
82
  });
72
83
  Object.freeze(newAST);
73
84
  }
85
+ flowEmitter.emit(EventManager.TYPES.AST_DEFINED, ast);
74
86
  return errors;
75
87
  }
76
88
 
77
89
  function exec(arg1, arg2, argN, cb) { // called to execute the flow
78
90
  /*jshint validthis: true */
79
- var parsedInput = inputParser(Array.prototype.slice.call(arguments), ast);
91
+ var args = Array.prototype.slice.call(arguments);
92
+ var env = {
93
+ execId: idGenerator.createUniqueId(),
94
+ args: args,
95
+ ast: ast,
96
+ flowEmitter: flowEmitter
97
+ };
98
+ env.name = ast.name || env.execId;
99
+ flowEmitter.emit(EventManager.TYPES.EXEC_FLOW_START, env); // hook
100
+ var parsedInput = inputParser(args, ast);
80
101
  var vCon = VContext.create(parsedInput.args, ast.inParams, ast.locals, this); // create var ctx with in args & locals
81
102
 
82
- var taskEnv = { // collect taskEnv for hook
83
- parsedInput: parsedInput,
84
- vCon: vCon,
85
- ast: ast, // for reference, will not be used further
86
- taskDefs: ast.tasks.slice(), // create copy
87
- outTaskDef: ast.outTask
88
- };
89
- reactEmitter.emit(EventManager.TYPES.EXEC_TASKS_PRECREATE, taskEnv); // hook
103
+ env.parsedInput = parsedInput;
104
+ env.options = mergeOptions(parsedInput.options);
105
+ env.vCon = vCon;
106
+ env.taskDefs = ast.tasks.slice(); // create copy
107
+ env.outTaskDef = Object.create(ast.outTask); // create copy
108
+ reactEmitter.emit(EventManager.TYPES.EXEC_TASKS_PRECREATE, env); // hook
90
109
 
91
- var tasks = taskEnv.taskDefs.map(tskutil.create);
110
+ var tasks = env.taskDefs.map(tskutil.create);
92
111
  var tasksByName = tskutil.nameTasks(tasks); // map names to working tasks
93
- var outTask = tskutil.createOutTask(taskEnv.outTaskDef, parsedInput.cb, tasks, vCon, mergeOptions(parsedInput.options));
112
+ var outTask = tskutil.createOutTask(env.outTaskDef, parsedInput.cb, tasks, vCon, env.options, env);
94
113
  var handleError = tskutil.createErrorHandler(vCon, outTask);
95
114
 
96
115
  function contExec() {
97
116
  if (!outTask.f) { return; } //stop execution, we already hit an error, f was cleared
98
117
  if (outTask.isReady()) return outTask.exec(); // all tasks done, exec cb, return
99
- tskutil.findReadyAndExec(vCon, tasks, tasksByName, handleError, contExec); //exec tasks that ready to run
118
+ tskutil.findReadyAndExec(vCon, tasks, tasksByName, handleError, contExec, env); //exec tasks that ready to run
100
119
  }
101
120
 
102
121
  tasks.forEach(function (t) {
103
122
  t.id = idGenerator.createUniqueId();
104
- t.flowEmitter = flowEmitter;
123
+ t.env = env;
105
124
  if (t.prepare) t.prepare(handleError, vCon, contExec, flowEmitter);
106
125
  }); // create callbacks
107
126
  contExec(); // start things off
@@ -0,0 +1,68 @@
1
+ 'use strict';
2
+
3
+ var react = require('../'); // require('react');
4
+ react.trackTasks(); // enable task tracking
5
+
6
+ var AST_EVENTS_RE = /^ast\./;
7
+ var TASK_EVENTS_RE = /^task\./;
8
+ var FLOW_EVENTS_RE = /^flow\./;
9
+
10
+ /**
11
+ Accumulator to make it easy to capture events
12
+
13
+ @example
14
+ var EventCollector = require('react/lib/event-collector');
15
+ var collector = new EventCollector();
16
+
17
+ collector.capture(); // capture all flow and task events for all react flows
18
+ collector.capture('flow.*'); // capture all flow events for all react flows
19
+ collector.capture(flowFn, 'task.*'); // capture task events on a flow
20
+ collector.capture(flowFn, 'flow.*'); // add capture flow events on a flow
21
+
22
+ var events = collector.list(); // retrieve the list of events
23
+ collector.clear(); // clear the list of events;
24
+ */
25
+ function EventCollector() {
26
+ this.events = [];
27
+ }
28
+
29
+ /**
30
+ register listener to capture events for a specific flow
31
+ @param flowFn the react flow function or can pass global react
32
+ @param eventId event id or wildcarded id
33
+ */
34
+ EventCollector.prototype.capture = function (flowFn, eventId) {
35
+ /*jshint validthis: true */
36
+ if (!eventId && typeof(flowFn) === 'string') { // only eventId provided
37
+ eventId = flowFn;
38
+ flowFn = react; // global react
39
+ } else if (!flowFn) flowFn = react; // global react
40
+ if (!eventId) eventId = '*'; // default to all
41
+ var emitter = flowFn.events;
42
+ var self = this;
43
+ function accumEvents(obj) {
44
+ var eventObject = {
45
+ event: this.event,
46
+ time: Date.now()
47
+ };
48
+ if (FLOW_EVENTS_RE.test(this.event)) {
49
+ eventObject.env = obj;
50
+ } else if (TASK_EVENTS_RE.test(this.event)) {
51
+ eventObject.task = obj;
52
+ } else if (AST_EVENTS_RE.test(this.event)) {
53
+ eventObject.ast = obj;
54
+ }
55
+ self.events.push(eventObject);
56
+ }
57
+ emitter.on(eventId, accumEvents);
58
+ };
59
+
60
+ EventCollector.prototype.list = function () {
61
+ return this.events;
62
+ };
63
+
64
+ EventCollector.prototype.clear = function () {
65
+ this.events = []; // clear
66
+ };
67
+
68
+ module.exports = EventCollector;
@@ -10,13 +10,24 @@ var EVENT_EMITTER2_CONFIG = {
10
10
 
11
11
  var TYPES = {
12
12
  // Flow monitoring events and their params
13
+ AST_DEFINED: 'ast.defined', // ast
14
+ FLOW_BEGIN: 'flow.begin', // env
13
15
  TASK_BEGIN: 'task.begin', // task
14
16
  TASK_COMPLETE: 'task.complete', // task
17
+ TASK_ERRORED: 'task.errored', // task
18
+ FLOW_COMPLETE: 'flow.complete', // env
19
+ FLOW_ERRORED: 'flow.errored', // env
15
20
 
16
21
  // Internal Hooks
22
+ EXEC_FLOW_START: 'exec.flow.start', // env
17
23
  EXEC_INPUT_PREPROCESS: 'exec.input.preprocess', // parsedInput
18
- EXEC_TASKS_PRECREATE: 'exec.tasks.precreate', // taskEnv
19
- EXEC_OUTTASK_CREATE: 'exec.outTask.create' // outTaskOptions
24
+ EXEC_TASKS_PRECREATE: 'exec.tasks.precreate', // env
25
+ EXEC_OUTTASK_CREATE: 'exec.outTask.create', // outTaskOptions
26
+ EXEC_TASK_START: 'exec.task.start', // task
27
+ EXEC_TASK_COMPLETE: 'exec.task.complete', // task
28
+ EXEC_TASK_ERRORED: 'exec.task.errored', // task
29
+ EXEC_FLOW_COMPLETE: 'exec.flow.complete', // env
30
+ EXEC_FLOW_ERRORED: 'exec.flow.errored' // env
20
31
  };
21
32
 
22
33
  /**
@@ -52,18 +63,10 @@ EventManager.prototype.emit = function (event, arg1, arg2, argN) {
52
63
  if (this.parent && this.parent.isEnabled()) this.parent.emit.apply(this.parent, arguments);
53
64
  };
54
65
 
55
- /**
56
- Emit an object augmented with standard fields.
57
- Copies object and adds standard fields:
58
- event: event type
59
- time: current time
60
- */
61
- EventManager.prototype.emitObject = function (event, object) {
62
- var evObj = Object.create(object); // create inherited copy version so origin is untouched
63
- evObj.event = event; // augment with the event type
64
- evObj.time = Date.now(); // augument with the time of the event
65
- this.emit(event, evObj);
66
+ EventManager.prototype.removeListener = function (event, listener) {
67
+ if (this.emitter) this.emitter.removeListener.apply(this.emitter, arguments);
66
68
  };
67
69
 
70
+
68
71
  module.exports = EventManager;
69
72
  module.exports.global = EventManager.create(); // create one top level emitter
@@ -5,23 +5,22 @@ var util = require('util');
5
5
 
6
6
  var STATUS = require('./status.js');
7
7
  var VContext = require('./vcon.js');
8
+ var EventManager = require('./event-manager.js');
8
9
 
9
10
  var OUTTASK_A_REQ = 'ast.outTask.a should be an array of string param names';
10
11
 
11
12
  function FinalCbFirstSuccTask(outTaskOptions) {
12
13
  var taskDef = outTaskOptions.taskDef;
13
- var cbFunc = outTaskOptions.cbFunc;
14
- var tasks = outTaskOptions.tasks;
15
- var vCon = outTaskOptions.vCon;
16
- var execOptions = outTaskOptions.execOptions;
17
- var retValue = outTaskOptions.retValue;
18
- if (typeof(cbFunc) !== 'function') throw new Error('callback is not a function');
14
+ if (typeof(outTaskOptions.cbFunc) !== 'function') throw new Error('callback is not a function');
19
15
  var self = this;
20
- Object.keys(taskDef).forEach(function (k) { self[k] = taskDef[k]; });
21
- this.f = cbFunc;
22
- this.tasks = tasks;
23
- this.vCon = vCon;
24
- this.retValue = retValue;
16
+ for (var k in taskDef) {
17
+ if (true) self[k] = taskDef[k]; // if to make jshint happy
18
+ }
19
+ this.f = outTaskOptions.cbFunc;
20
+ this.tasks = outTaskOptions.tasks;
21
+ this.vCon = outTaskOptions.vCon;
22
+ this.retValue = outTaskOptions.retValue;
23
+ this.env = outTaskOptions.env;
25
24
  }
26
25
 
27
26
  function format_error(errmsg, obj) {
@@ -49,11 +48,15 @@ FinalCbFirstSuccTask.prototype.isReady = function () {
49
48
  FinalCbFirstSuccTask.prototype.exec = function (err) {
50
49
  if (!this.f) return; //must have already been called
51
50
  if (err) {
51
+ this.env.error = err;
52
+ this.env.flowEmitter.emit(EventManager.TYPES.EXEC_FLOW_ERRORED, this.env);
52
53
  this.f.call(null, err); //call the final callback with the first error hit
53
54
  } else { // no error, call with args
54
55
  var vCon = this.vCon;
55
56
  var finalArgs = this.a.map(function (k) { return vCon.getVar(k); });
56
57
  finalArgs.unshift(null); //unshift err=null to front
58
+ this.env.results = finalArgs;
59
+ this.env.flowEmitter.emit(EventManager.TYPES.EXEC_FLOW_COMPLETE, this.env);
57
60
  this.f.apply(null, finalArgs);
58
61
  }
59
62
  this.f = null; // prevent multiple calls