react 0.1.2 → 0.2.0

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/.npmignore CHANGED
@@ -2,4 +2,5 @@ node_modules
2
2
  npm-debug.log
3
3
  ./npmrc
4
4
  todo.md
5
- notes.md
5
+ notes.md
6
+ oldExamples
package/README.md CHANGED
@@ -25,13 +25,13 @@ It takes inspiration from several projects including:
25
25
 
26
26
  ## Goals
27
27
 
28
+ - Minimize boilerplate code needed for working with asynchronous functions
29
+ - Minimize the need to customize your code simply to use async flow control. The use of a flow control module ideally should not affect the way you write your code, it should only help take over some of the burden.
28
30
  - Improved error and exception handling
29
31
  - Provide useful stack traces and context information for easier debugging
30
- - Minimize boilerplate code needed for working with asynchronous functions
31
32
  - Make code more readable and easier to understand which should translate to less defects
32
33
  - Provide the right level of abstraction to make it easier to refactor code, without being too magical
33
34
  - Allow the mixing of pure functions, method calls, and callback style functions in the flow
34
- - Minimize the need to customize your code simply to use async flow control. The use of a flow control module ideally should not affect the way you write your code, it should only help take over some of the burden.
35
35
 
36
36
  ## Supports
37
37
 
@@ -61,9 +61,9 @@ To reduce the boilerplate code needed and improve error handling, React automati
61
61
 
62
62
  ## Design
63
63
 
64
- - Parse and validate ad module load time
64
+ - Parse and validate DSL rules at module load time
65
65
  - Validate the flow AST at module load time - determine if dependencies can all be met as defined
66
- - Execute the flow AST by calling the function with params
66
+ - Execute the flow AST by calling the function with arguments
67
67
 
68
68
  ## Installing
69
69
 
@@ -75,8 +75,8 @@ Pull from github - http://github.com/jeffbski/react
75
75
 
76
76
  ## Examples
77
77
 
78
- 1. [Direct AST](#directAST)
79
- 2. [Using Function Str DSL](#fstr)
78
+ 1. [Default DSL](#defaultDSL)
79
+ 2. [Direct AST](#directAST)
80
80
  3. [Using pseudocode DSL](#pcode)
81
81
  4. [Using jquery-like chaining DSL](#chain)
82
82
 
@@ -85,6 +85,44 @@ These live in the examples folder so they are ready to run.
85
85
  Also see test/module-use.test.js for more examples as well
86
86
  as the specific tests for the DSL you want to use.
87
87
 
88
+ <a name="defaultDSL"/>
89
+ ### Example using default DSL
90
+
91
+ ```javascript
92
+ // in your foo module
93
+ var react = require('react');
94
+
95
+ // some normal async and sync functions
96
+ function loadUser(uid, cb){ }
97
+ function loadFile(filename, cb){ }
98
+ function markdown(filedata) { }
99
+ function writeOutput(html, user, cb){ }
100
+ function loadEmailTemplate(cb) { }
101
+ function customizeEmail(user, emailHtml, cb) { }
102
+ function deliverEmail(custEmailHtml, cb) { }
103
+
104
+ // define fn, glue together with react, it will parallelize
105
+ // starts with name and in/out params, then the tasks
106
+ var loadAndSend = react('loadAndSend', 'uid, filename, cb -> err, user',
107
+ loadUser, 'uid, cb -> err, user',
108
+ loadFile, 'filename, cb -> err, filemd',
109
+ markdown, 'filemd -> html', // no cb, implies sync fn
110
+ writeOutput, 'html, user, cb -> err, htmlBytesWritten',
111
+ loadEmailTemplate, 'cb -> err, emailmd',
112
+ markdown, 'emailmd -> emailHtml', // no cb, implies sync fn
113
+ customizeEmail, 'user, emailHtml, cb -> err, custEHtml',
114
+ deliverEmail, 'custEHtml, cb -> err, custBytesWritten'
115
+ );
116
+ exports.loadAndSend = loadAndSend; // is a normal fn created by react
117
+
118
+ // in a different module far far away, use this as any other node function
119
+ var foo = require('foo');
120
+ foo.loadAndSend(100, 'bar.md', function (err, user) {
121
+ // tasks were parallelized based on their depedencies
122
+ }
123
+ ```
124
+
125
+
88
126
  <a name="directAST"/>
89
127
  ### Example directly using AST
90
128
 
@@ -204,6 +242,7 @@ fn(2, 3, function (err, m, s) {
204
242
 
205
243
  ## Status
206
244
 
245
+ - 2012-01-10 - Create default DSL for react()
207
246
  - 2011-12-21 - Refactor from ground up with tests, changes to the interfaces
208
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.
209
248
 
@@ -214,12 +253,13 @@ ok ast.test.js .................... 10/10
214
253
  ok cb-task.test.js ................ 31/31
215
254
  ok chain.test.js .................. 56/56
216
255
  ok core.test.js ................... 98/98
256
+ ok dsl.test.js .................... 58/58
217
257
  ok event-manager.test.js .......... 13/13
218
258
  ok exec-options.test.js ............. 3/3
219
259
  ok finalcb-task.test.js ............. 5/5
220
260
  ok fstr.test.js ................... 64/64
221
261
  ok input-parser.test.js ........... 15/15
222
- ok module-use.test.js ............. 55/55
262
+ ok module-use.test.js ............. 64/64
223
263
  ok pcode.test.js .................. 65/65
224
264
  ok ret-task.test.js ............... 31/31
225
265
  ok task.test.js ..................... 1/1
@@ -227,7 +267,7 @@ ok validate-cb-task.test.js ......... 6/6
227
267
  ok validate-ret-task.test.js ........ 7/7
228
268
  ok validate.test.js ............... 26/26
229
269
  ok vcon.test.js ................... 42/42
230
- total ........................... 545/545
270
+ total ........................... 613/613
231
271
 
232
272
  ok
233
273
  ```
@@ -0,0 +1,40 @@
1
+ 'use strict';
2
+ /*jshint white: false */
3
+
4
+ var react = require('../'); // require('react');
5
+
6
+ function loadUser(uid, cb){ setTimeout(cb, 100, null, "User"+uid); }
7
+ function loadFile(filename, cb){ setTimeout(cb, 100, null, 'Filedata'+filename); }
8
+ function markdown(filedata) { return 'html'+filedata; }
9
+ function prepareDirectory(outDirname, cb){ setTimeout(cb, 200, null, 'dircreated-'+outDirname); }
10
+ function writeOutput(html, user, cb){ setTimeout(cb, 300, null, html+'_bytesWritten'); }
11
+ function loadEmailTemplate(cb) { setTimeout(cb, 50, null, 'emailmd'); }
12
+ function customizeEmail(user, emailHtml, cb) { return 'cust-'+user+emailHtml; }
13
+ function deliverEmail(custEmailHtml, cb) { setTimeout(cb, 100, null, 'delivered-'+custEmailHtml); }
14
+
15
+ function useHtml(err, html, user, bytesWritten) {
16
+ if (err) {
17
+ console.log('***Error: %s', err);
18
+ return;
19
+ }
20
+ console.log('final result: %s, user: %s, written:%s', html, user, bytesWritten);
21
+ }
22
+
23
+ // define fn, glue together with react, it will parallelize
24
+ // starts with name and in/out params, then the tasks
25
+ var loadAndSave = react('loadAndSave', 'fName, uid, outDir, cb -> err, html, user, bytes',
26
+ loadUser, 'uid -> err, user', // calling async loadUser with uid, cb called with err and user
27
+ loadFile, 'filename -> err, filedata',
28
+ markdown, 'filedata -> returns html', // using a sync function
29
+ prepareDirectory, 'outDirname -> err, dircreated',
30
+ writeOutput, 'html, user -> err, bytesWritten', { after: prepareDirectory }, // only after prepareDirectory done
31
+ loadEmailTemplate, ' -> err, emailmd',
32
+ markdown, 'emailmd -> returns emailHtml', // using a sync function
33
+ customizeEmail, 'user, emailHtml -> returns custEmailHtml',
34
+ deliverEmail, 'custEmailHtml -> err, deliveredEmail', { after: writeOutput } // only after writeOutput is done
35
+ ); // callback output params
36
+
37
+ //in another module you can use this as any other exported fn
38
+ loadAndSave('file.md', 100, '/tmp/foo', useHtml); // executing the flow
39
+
40
+
package/lib/core.js CHANGED
@@ -45,6 +45,7 @@ function reactFactory() {
45
45
  flowEmitter.parent = reactEmitter;
46
46
 
47
47
  var ast = {
48
+ name: undefined,
48
49
  inParams: [],
49
50
  tasks: [],
50
51
  outTask: {},
package/lib/dsl.js ADDED
@@ -0,0 +1,122 @@
1
+ 'use strict';
2
+ /*jshint regexp: false */
3
+
4
+ var sprintf = require('sprintf').sprintf;
5
+ var core = require('./core.js');
6
+ var parse = require('./parse.js');
7
+ var tutil = require('./task.js');
8
+
9
+ var INOUT_PARAMS_NO_MATCH = 'params in wrong format, wanted "foo, bar cb -> err, baz" - found: %s';
10
+ var EXTRA_TASKARG = 'extra unmatched task arg: %s';
11
+
12
+ var CB_NAMES_RE = /^cb|callback$/i; //cb, Cb, CB, callback, Callback
13
+ var ERR_NAMES_RE = /^err$/i; // err, ERR, Err, ...
14
+
15
+ function filterOutTrailingCbParam(args) { // if has trailing cb | callback param, filter it out
16
+ if (args.length && args[args.length - 1].match(CB_NAMES_RE)) args.pop();
17
+ return args;
18
+ }
19
+
20
+ function filterOutLeadingErrParam(args) { // if leading err param, filter it out
21
+ if (args.length && args[0].match(ERR_NAMES_RE)) args.shift();
22
+ return args;
23
+ }
24
+
25
+ var inOutDefParse = {
26
+ regex: /^([^\-]*)(->\s*(.*))?$/i,
27
+ fn: function (m) {
28
+ var inParams = parse.splitTrimFilterArgs(m[1]);
29
+ var lastParam = inParams[inParams.length - 1];
30
+ var type = (lastParam && CB_NAMES_RE.test(lastParam)) ? 'cb' : 'ret';
31
+ var outParams = parse.splitTrimFilterArgs(m[3]);
32
+ return {
33
+ type: type,
34
+ inDef: filterOutTrailingCbParam(inParams),
35
+ outDef: filterOutLeadingErrParam(outParams)
36
+ };
37
+ }
38
+ };
39
+
40
+ function parseInOutParams(str) {
41
+ var objDef = parse.parseStr(str, [inOutDefParse], INOUT_PARAMS_NO_MATCH);
42
+ objDef.inDef = filterOutTrailingCbParam(objDef.inDef);
43
+ return objDef;
44
+ }
45
+
46
+ function parseTasks(arr) {
47
+ var tasks = [];
48
+ var fn, obj, result;
49
+ while (arr.length >= 2) {
50
+ obj = {};
51
+ fn = arr.shift();
52
+ result = parseInOutParams(arr.shift());
53
+ if (typeof(arr[0]) === 'object') obj = arr.shift(); // has options, use as obj
54
+ obj.f = fn;
55
+ obj.a = result.inDef;
56
+ var type = result.type;
57
+ obj.out = result.outDef;
58
+ obj.type = type;
59
+ tasks.push(obj);
60
+ }
61
+ if (arr.length) throw new Error(sprintf(EXTRA_TASKARG, arr[0]));
62
+ return tasks;
63
+ }
64
+
65
+ /**
66
+ Parse the variable arguments into in/out params, tasks, options
67
+ */
68
+ function parseVargs(vargs) {
69
+ var inOutParamStr = vargs.shift() || '';
70
+ // if last arg is object, pop it off as options
71
+ var options = (vargs.length && typeof(vargs[vargs.length - 1]) === 'object') ? vargs.pop() : { };
72
+ var taskDefArr = vargs; // rest are for the tasks
73
+ var defObj = {
74
+ inOutParamStr: inOutParamStr,
75
+ taskDefArr: taskDefArr,
76
+ options: options
77
+ };
78
+ return defObj;
79
+ }
80
+
81
+
82
+ function dslDefine(name, arg1, arg2, argN) {
83
+ var reactFn = core();
84
+ var defObj = parseVargs(Array.prototype.slice.call(arguments, 1)); // name, already used
85
+ var inOutDef = parseInOutParams(defObj.inOutParamStr);
86
+ var ast = {
87
+ name: name,
88
+ inParams: inOutDef.inDef,
89
+ tasks: parseTasks(defObj.taskDefArr),
90
+ outTask: { a: inOutDef.outDef }
91
+ };
92
+ if (defObj.options) Object.keys(defObj.options).forEach(function (k) { ast[k] = defObj.options[k]; });
93
+ var errors = reactFn.setAndValidateAST(ast);
94
+ if (errors.length) {
95
+ var errorStr = errors.join('\n');
96
+ throw new Error(errorStr);
97
+ }
98
+ return reactFn;
99
+ }
100
+
101
+ function selectFirst(name, arg1, arg2, argN) {
102
+ var reactFn = core();
103
+ var defObj = parseVargs(Array.prototype.slice.call(arguments, 1)); // name, already used
104
+ var inOutDef = parseInOutParams(defObj.inOutParamStr);
105
+ var tasks = tutil.serializeTasks(parseTasks(defObj.taskDefArr));
106
+ var ast = {
107
+ name: name,
108
+ inParams: inOutDef.inDef,
109
+ tasks: tasks,
110
+ outTask: { type: 'finalcbFirst', a: inOutDef.outDef },
111
+ };
112
+ if (defObj.options) Object.keys(defObj.options).forEach(function (k) { ast[k] = defObj.options[k]; });
113
+ var errors = reactFn.setAndValidateAST(ast);
114
+ if (errors.length) {
115
+ var errorStr = errors.join('\n');
116
+ throw new Error(errorStr);
117
+ }
118
+ return reactFn;
119
+ }
120
+
121
+ module.exports = dslDefine;
122
+ module.exports.selectFirst = selectFirst;
package/lib/parse.js CHANGED
@@ -3,6 +3,7 @@
3
3
  var sprintf = require('sprintf').sprintf;
4
4
 
5
5
  function splitTrimFilterArgs(commaSepArgs) { //parse 'one, two' into ['one', 'two']
6
+ if (!commaSepArgs) return [];
6
7
  return commaSepArgs.split(',') //split on commas
7
8
  .map(function (s) { return s.trim(); }) //trim
8
9
  .filter(function (s) { return (s); }); //filter out empty strings
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.1.2",
4
+ "version": "0.2.0",
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,11 +2,11 @@
2
2
 
3
3
  var core = require('./lib/core.js');
4
4
 
5
- module.exports = core;
6
- module.exports.options = core.options;
7
- module.exports.events = core.events; // top level emitter
5
+ module.exports = require('./lib/dsl.js'); // core + default dsl
6
+ module.exports.options = core.options; // global react options
7
+ module.exports.events = core.events; // global react event emitter
8
8
 
9
- // interfaces
9
+ // additional interfaces
10
10
  module.exports.fstrDefine = require('./lib/fstr.js');
11
11
  module.exports.pcodeDefine = require('./lib/pcode.js');
12
12
  module.exports.chainDefine = require('./lib/chain.js');
@@ -0,0 +1,237 @@
1
+ 'use strict';
2
+
3
+ var test = require('tap').test;
4
+ var sprintf = require('sprintf').sprintf;
5
+
6
+ var react = require('../'); // require('react');
7
+
8
+ function falpha() { }
9
+ function fbeta() { }
10
+
11
+ test('module exports is a fn with properties', function (t) {
12
+ t.type(react, 'function', 'has define by DSL method'); //
13
+ t.type(react.selectFirst, 'function', 'has selectFirst define method');
14
+ t.end();
15
+ });
16
+
17
+ test('no arguments -> empty name, inParams, tasks, outTask', function (t) {
18
+ var r = react();
19
+ t.equal(r.ast.name, undefined);
20
+ t.deepEqual(r.ast.inParams, []);
21
+ t.deepEqual(r.ast.tasks, []);
22
+ t.deepEqual(r.ast.outTask, { a: [], type: 'finalcb' });
23
+ t.end();
24
+ });
25
+
26
+
27
+ test('empty first string -> empty name, inParams, tasks, outTask', function (t) {
28
+ var r = react('');
29
+ t.equal(r.ast.name, '');
30
+ t.deepEqual(r.ast.inParams, []);
31
+ t.deepEqual(r.ast.tasks, []);
32
+ t.deepEqual(r.ast.outTask, { a: [], type: 'finalcb' });
33
+ t.end();
34
+ });
35
+
36
+
37
+ test('single first string -> name, inParams["foo"], empty tasks, outTask', function (t) {
38
+ var r = react('foo');
39
+ t.equal(r.ast.name, 'foo');
40
+ t.deepEqual(r.ast.inParams, []);
41
+ t.deepEqual(r.ast.tasks, []);
42
+ t.deepEqual(r.ast.outTask, { a: [], type: 'finalcb' });
43
+ t.end();
44
+ });
45
+
46
+ test('triple first string -> inParams["foo", "bar", "baz"], empty tasks, outTask',
47
+ function (t) {
48
+ var r = react('myName', ' foo, bar,baz ');
49
+ t.equal(r.ast.name, 'myName');
50
+ t.deepEqual(r.ast.inParams, ['foo', 'bar', 'baz']);
51
+ t.deepEqual(r.ast.tasks, []);
52
+ t.deepEqual(r.ast.outTask, { a: [], type: 'finalcb' });
53
+ t.end();
54
+ });
55
+
56
+ test('single task, single out params', function (t) {
57
+ var r = react('myName', 'cb -> err, c',
58
+ falpha, 'a, b, cb -> err, c'
59
+ );
60
+ t.deepEqual(r.ast.inParams, []);
61
+ t.deepEqual(r.ast.tasks, [
62
+ { f: falpha, a: ['a', 'b'], out: ['c'], type: 'cb', name: 'falpha'}
63
+ ]);
64
+ t.deepEqual(r.ast.outTask, { a: ['c'], type: 'finalcb' });
65
+ t.end();
66
+ });
67
+
68
+ test('single task, err and out params', function (t) {
69
+ var r = react('myName', 'cb -> err, c',
70
+ falpha, 'a, b, cb -> err, c'
71
+ );
72
+ t.deepEqual(r.ast.inParams, []);
73
+ t.deepEqual(r.ast.tasks, [
74
+ { f: falpha, a: ['a', 'b'], out: ['c'], type: 'cb', name: 'falpha'}
75
+ ]);
76
+ t.deepEqual(r.ast.outTask, { a: ['c'], type: 'finalcb' });
77
+ t.end();
78
+ });
79
+
80
+ test('single task, ERR and out params', function (t) {
81
+ var r = react('myName', 'cb -> ERR, c',
82
+ falpha, 'a, b, cb -> ERR, c'
83
+ );
84
+ t.deepEqual(r.ast.inParams, []);
85
+ t.deepEqual(r.ast.tasks, [
86
+ { f: falpha, a: ['a', 'b'], out: ['c'], type: 'cb', name: 'falpha'}
87
+ ]);
88
+ t.deepEqual(r.ast.outTask, { a: ['c'], type: 'finalcb' });
89
+ t.end();
90
+ });
91
+
92
+ test('cb used in defs is simply ignored', function (t) {
93
+ var r = react('myName', 'a, b, cb -> err, c',
94
+ falpha, 'a, b, cb -> err, c'
95
+ );
96
+ t.deepEqual(r.ast.inParams, ['a', 'b']);
97
+ t.deepEqual(r.ast.tasks, [
98
+ { f: falpha, a: ['a', 'b'], out: ['c'], type: 'cb', name: 'falpha'}
99
+ ]);
100
+ t.deepEqual(r.ast.outTask, { a: ['c'], type: 'finalcb' });
101
+ t.end();
102
+ });
103
+
104
+ test('callback used in defs is simply ignored', function (t) {
105
+ var r = react('myName', 'a, b, callback -> err, c',
106
+ falpha, 'a, b, callback -> err, c'
107
+ );
108
+ t.deepEqual(r.ast.inParams, ['a', 'b']);
109
+ t.deepEqual(r.ast.tasks, [
110
+ { f: falpha, a: ['a', 'b'], out: ['c'], type: 'cb', name: 'falpha'}
111
+ ]);
112
+ t.deepEqual(r.ast.outTask, { a: ['c'], type: 'finalcb' });
113
+ t.end();
114
+ });
115
+
116
+ test('two inputs, two tasks, two out params', function (t) {
117
+ var r = react('myName', 'a, b, cb -> err, c, d',
118
+ falpha, 'a, b, cb -> err, c',
119
+ fbeta, 'a, b, cb -> err, d, e'
120
+ );
121
+ t.deepEqual(r.ast.inParams, ['a', 'b']);
122
+ t.deepEqual(r.ast.tasks, [
123
+ { f: falpha, a: ['a', 'b'], out: ['c'], type: 'cb', name: 'falpha'},
124
+ { f: fbeta, a: ['a', 'b'], out: ['d', 'e'], type: 'cb', name: 'fbeta'}
125
+ ]);
126
+ t.deepEqual(r.ast.outTask, { a: ['c', 'd'], type: 'finalcb' });
127
+ t.end();
128
+ });
129
+
130
+ test('two inputs, two tasks, two out params, options', function (t) {
131
+ var r = react('myName', 'a, b, cb -> err, c, d',
132
+ falpha, 'a, b, cb -> err, c',
133
+ fbeta, 'a, b, cb -> err, d, e',
134
+ { name: 'myflow', otherOptFoo: 'foo'}
135
+ );
136
+ t.deepEqual(r.ast.inParams, ['a', 'b']);
137
+ t.deepEqual(r.ast.tasks, [
138
+ { f: falpha, a: ['a', 'b'], out: ['c'], type: 'cb', name: 'falpha'},
139
+ { f: fbeta, a: ['a', 'b'], out: ['d', 'e'], type: 'cb', name: 'fbeta'}
140
+ ]);
141
+ t.deepEqual(r.ast.outTask, { a: ['c', 'd'], type: 'finalcb' });
142
+ t.equal(r.ast.name, 'myflow', 'name should match if supplied');
143
+ t.equal(r.ast.otherOptFoo, 'foo', 'other options should pass through');
144
+ t.end();
145
+ });
146
+
147
+ test('two inputs, two mixed tasks, two out params', function (t) {
148
+ var r = react('myName', 'a, b, cb -> err, c, d',
149
+ falpha, 'a, b, cb -> err, c',
150
+ fbeta, 'a, b -> d'
151
+ );
152
+ t.deepEqual(r.ast.inParams, ['a', 'b']);
153
+ t.deepEqual(r.ast.tasks, [
154
+ { f: falpha, a: ['a', 'b'], out: ['c'], type: 'cb', name: 'falpha'},
155
+ { f: fbeta, a: ['a', 'b'], out: ['d'], type: 'ret', name: 'fbeta'}
156
+ ]);
157
+ t.deepEqual(r.ast.outTask, { a: ['c', 'd'], type: 'finalcb' });
158
+ t.end();
159
+ });
160
+
161
+
162
+ test('two inputs, two mixed tasks, two out params, opts', function (t) {
163
+ var r = react('myName', 'a, b, cb -> err, c, d',
164
+ falpha, 'a, cb -> err, c', { after: fbeta },
165
+ fbeta, 'a, b -> d'
166
+ );
167
+ t.deepEqual(r.ast.inParams, ['a', 'b']);
168
+ t.deepEqual(r.ast.tasks, [
169
+ { after: ['fbeta'], f: falpha, a: ['a'], out: ['c'], type: 'cb', name: 'falpha'},
170
+ { f: fbeta, a: ['a', 'b'], out: ['d'], type: 'ret', name: 'fbeta'}
171
+ ]);
172
+ t.deepEqual(r.ast.outTask, { a: ['c', 'd'], type: 'finalcb' });
173
+ t.end();
174
+ });
175
+
176
+
177
+ // Object use
178
+ test('object prop task params', function (t) {
179
+ var r = react('myName', 'a, b, cb -> err, c, e',
180
+ falpha, 'a, b.cat, cb -> err, c',
181
+ fbeta, 'c.dog, b -> d',
182
+ 'd.egg', 'c, cb -> err, e'
183
+ );
184
+ t.deepEqual(r.ast.inParams, ['a', 'b']);
185
+ t.deepEqual(r.ast.tasks, [
186
+ { f: falpha, a: ['a', 'b.cat'], out: ['c'], type: 'cb', name: 'falpha'},
187
+ { f: fbeta, a: ['c.dog', 'b'], out: ['d'], type: 'ret', name: 'fbeta'},
188
+ { f: 'd.egg', a: ['c'], out: ['e'], type: 'cb', name: 'd.egg'}
189
+ ]);
190
+ t.deepEqual(r.ast.outTask, { a: ['c', 'e'], type: 'finalcb' });
191
+ t.end();
192
+ });
193
+
194
+ test('extra arg throws error', function (t) {
195
+ var fn = function () {
196
+ var r = react('myName', 'a, b, cb -> err, c, d',
197
+ falpha, 'a -> err, c', { after: fbeta },
198
+ fbeta, 'a, b -> returns d',
199
+ 'extraBadArg'
200
+ );
201
+ };
202
+ t.throws(fn, new Error('extra unmatched task arg: extraBadArg'));
203
+ t.end();
204
+ });
205
+
206
+ test('not enough args throws error', function (t) {
207
+ var fn = function () {
208
+ var r = react('myName', 'a, b, cb -> c, d',
209
+ falpha, 'a -> err, c', { after: fbeta },
210
+ fbeta
211
+ );
212
+ };
213
+ t.throws(fn, new Error(sprintf('extra unmatched task arg: %s', fbeta)));
214
+ t.end();
215
+ });
216
+
217
+
218
+
219
+ // selectFirst
220
+
221
+ test('selectFirst', function (t) {
222
+ var r = react.selectFirst('myName', 'a, b, cb -> c',
223
+ falpha, 'a, b, cb -> err, c',
224
+ fbeta, 'a, b -> c',
225
+ { otherOptFoo: 'foo'}
226
+ );
227
+ t.equal(r.ast.name, 'myName');
228
+ t.deepEqual(r.ast.inParams, ['a', 'b']);
229
+ t.deepEqual(r.ast.tasks, [
230
+ { f: falpha, a: ['a', 'b'], out: ['c'], type: 'cb', name: 'falpha'},
231
+ { f: fbeta, a: ['a', 'b'], out: ['c'], type: 'ret', name: 'fbeta', after: ['falpha']}
232
+ ]);
233
+ t.deepEqual(r.ast.outTask, { type: 'finalcbFirst', a: ['c'] });
234
+ t.equal(r.ast.name, 'myName', 'name should match if supplied');
235
+ t.equal(r.ast.otherOptFoo, 'foo', 'other options should pass through');
236
+ t.end();
237
+ });
@@ -9,13 +9,16 @@ var react = require('../react');
9
9
  @example
10
10
  var react = require('react');
11
11
  react.options.an_option = 'something';
12
-
13
- var loadAndSave = react.fstrDefine('one, two, cb -> err, result1, result2',
14
- foo, 'one -> err, cat',
15
- bar, 'two, cat -> err, dog',
16
- baz, 'dog -> err, result1',
17
- bum, 'dog -> err, result2');
18
-
12
+
13
+ // define function
14
+ var loadAndSave = react('myName', 'one, two, cb -> err, result1, result2',
15
+ foo, 'one, cb -> err, cat',
16
+ bar, 'two, cat, cb -> err, dog',
17
+ baz, 'dog, cb -> err, result1',
18
+ bum, 'dog, cb -> err, result2');
19
+
20
+ // OR using AST
21
+
19
22
  var loadAndSave = react();
20
23
  loadAndSave.setAndValidateAST({
21
24
  inParams: ['one', 'two'],
@@ -26,11 +29,11 @@ var react = require('../react');
26
29
  //if you want to listen to task completion events
27
30
  loadAndSave.events.on('task.complete', function (taskObj) { });
28
31
 
29
- loadAndSave(1,2,cb);
32
+ loadAndSave(1,2,cb); // execute like any other function
30
33
  */
31
34
 
32
35
  test('module exports an function object with properties', function (t) {
33
- t.type(react, 'function', 'is a core constructor function');
36
+ t.type(react, 'function', 'is a core constructor and default dsl function');
34
37
  t.type(react.options, 'object', 'has property for global react options');
35
38
  t.type(react.fstrDefine, 'function', 'has fn property for using fstr dsl');
36
39
  t.type(react.pcodeDefine, 'function', 'has fn property for using pcode dsl');
@@ -47,15 +50,7 @@ test('calling react constructor function creates new function with ast', functio
47
50
  t.type(r.ast, 'object', 'is object for inspecting AST');
48
51
  t.deepEqual(r.ast.inParams, [], 'ast.inParams should return empty array');
49
52
  t.deepEqual(r.ast.tasks, [], 'ast.tasks() should return empty array');
50
- t.deepEqual(r.ast.outTask, {}, 'should return empty object');
51
- t.end();
52
- });
53
-
54
- test('deprecated react API should throw to help debugging', function (t) {
55
- function badUse() {
56
- var r = react('filename, uid, outDirname, cb'); //this should throw
57
- }
58
- t.throws(badUse, new Error('react() takes no args, check API'));
53
+ t.deepEqual(r.ast.outTask, { a: [], type: 'finalcb' });
59
54
  t.end();
60
55
  });
61
56
 
@@ -78,6 +73,57 @@ test('setAndValidateAST sets the ast and validates returning errors', function (
78
73
  t.end();
79
74
  });
80
75
 
76
+ test('use react() default DSL from module', function (t) {
77
+ t.plan(3);
78
+ function multiply(a, b, cb) { cb(null, a * b); }
79
+ function add(a, b, cb) { cb(null, a + b); }
80
+ var fn = react('multiplyAdd', 'a, b, cb -> err, m, s',
81
+ multiply, 'a, b, cb -> err, m',
82
+ add, 'm, a, cb -> err, s'
83
+ );
84
+
85
+ fn(2, 3, function (err, m, s) {
86
+ t.deepEqual(err, null, 'should not be any error');
87
+ t.equal(m, 6);
88
+ t.equal(s, 8);
89
+ t.end();
90
+ });
91
+ });
92
+
93
+ test('use react.selectFirst() default DSL with events', function (t) {
94
+ t.plan(7);
95
+ function noSuccess(a, b, cb) {
96
+ setTimeout(function () { cb(null); }, 100); // returns undefined result
97
+ }
98
+ function noSuccessNull(a, b, cb) { cb(null, null); } // returns null result
99
+ function add(a, b, cb) { cb(null, a + b); }
100
+
101
+ var events = [];
102
+ function accumEvents(task) {
103
+ events.push(task);
104
+ }
105
+
106
+ var fn = react.selectFirst('mySelectFirst', 'a, b, cb -> err, c',
107
+ noSuccess, 'a, b, cb -> err, c',
108
+ noSuccessNull, 'a, b, cb -> err, c',
109
+ add, 'a, b, cb -> err, c',
110
+ noSuccess, 'a, b, cb -> err, c'
111
+ );
112
+
113
+ fn.events.on('task.complete', accumEvents);
114
+
115
+ fn(2, 3, function (err, c) {
116
+ t.deepEqual(err, null, 'should not be any error');
117
+ t.equal(c, 5);
118
+ t.equal(events.length, 3, 'should have seen two task compl events');
119
+ t.equal(events[0].name, 'noSuccess', 'name matches');
120
+ t.equal(events[1].name, 'noSuccessNull', 'name matches');
121
+ t.equal(events[2].name, 'add', 'name matches');
122
+ t.deepEqual(events[2].results, [5], 'results match');
123
+ t.end();
124
+ });
125
+ });
126
+
81
127
 
82
128
  test('use pcodeDefine from module', function (t) {
83
129
  t.plan(3);