Comparing version 0.2.2 to 0.3.0
@@ -1,274 +0,131 @@ | ||
var util = require('util'); | ||
var async = require('async'); | ||
var us = require('underscore'); | ||
var errors = require('common-errors'); | ||
var Execution = require('./execution'); | ||
var Task = require('./task'); | ||
var AsyncTask = require('./async-task'); | ||
var SyncTask = require('./sync-task'); | ||
var FlowTaskError = require('./flowTaskError'); | ||
var FlowTaskArgumentNullError = require('./flowTaskArgumentNullError'); | ||
var Task = require('./flowTask').Task; | ||
var AsyncTask = require('./flowTask').AsyncTask; | ||
var SyncTask = require('./flowTask').SyncTask; | ||
module.exports = { | ||
flow: function flow(data, tasks, callback){ | ||
var workflow = new Workflow(data, tasks, callback); | ||
workflow.execute(); | ||
} | ||
} | ||
var Workflow = function Workflow(data, tasks, callback){ | ||
if(arguments.length == 2){ | ||
if(typeof tasks == 'function'){ | ||
callback = tasks; | ||
tasks = data; | ||
data = undefined; | ||
var Flow = module.exports.Flow = function Flow(context_name, tasks) { | ||
var self = this; | ||
var args = arguments; | ||
self._initialize = function _initialize(){ | ||
if(self.parent) { | ||
if(!context_name) throw new errors.ArgumentNull('context_name'); | ||
} else { | ||
if(args.length == 1) tasks = context_name, context_name = undefined; | ||
} | ||
} else if(arguments.length == 1){ | ||
tasks = data; | ||
callback = undefined; | ||
data = undefined; | ||
} | ||
this.data = data; | ||
this.tasks = tasks; | ||
this.callback = callback || function(){}; | ||
} | ||
self.context_name = context_name; | ||
self.tasks = us.extend(self.tasks, tasks || {}); | ||
Workflow.prototype.execute = function execute(contextName) { | ||
var self = this; | ||
self.contextName = contextName; | ||
var exec = function(data, cb){ | ||
var context; | ||
if(contextName) context = data[contextName]; | ||
self._initialize_tasks(); | ||
} | ||
var newTasks; | ||
if(data) newTasks = us.object(Object.keys(data), us.map(Object.keys(data), function(key){ return function(cb){ cb(null, data[key]); } })) | ||
if(context) us.extend(newTasks, us.object(Object.keys(context), us.map(Object.keys(context), function(key){ return function(cb){ cb(null, context[key]); } }))); | ||
if(self.tasks) us.extend(newTasks, us.object(Object.keys(self.tasks), us.map(Object.keys(self.tasks), function(taskName){ | ||
var taskPlan = self.interpretTask(taskName, data, context); | ||
return self.executeTaskPlan(taskPlan, taskName); | ||
} ))); | ||
return async.auto(newTasks, function(err, results){ | ||
if(contextName) cb(err, us.pick(results, Object.keys(self.tasks))); | ||
else cb(err, results); | ||
}); | ||
}; | ||
if (this.data instanceof Array) async.map(this.data, exec, this.callback); | ||
else exec(this.data, this.callback); | ||
self.tasks = {}; | ||
self.flows = {}; | ||
self._explicit_reqs = []; | ||
} | ||
Workflow.prototype.interpretTask = function interpretTask(taskName, data, context) { | ||
var self = this; | ||
var taskArgs = Array.isArray(self.tasks[taskName]) ? new AsyncTask(self.tasks[taskName]) : self.tasks[taskName]; | ||
this.tasks[taskName] = taskArgs; | ||
var tasks = self.tasks; | ||
us.extend(Flow, { | ||
Task: AsyncTask, | ||
Fn: SyncTask | ||
}); | ||
var fn; //function to call within each async.auto task (does the work). | ||
var fnIndex; //index where fn was found. | ||
var args = []; //arguments to pass to each function during task execution | ||
var autoTask = []; //array value for each async.auto task | ||
var task = taskArgs instanceof Task ? taskArgs : null; | ||
var interpretSubFlow = function(subFlow){ | ||
if(fn) throw new FlowTaskError(taskName, "A task may have a SubFlow (index " + i + ") or a function call (index " + i + "), but not both. " + fnIndex + " and " + i + ")."); | ||
if(taskArgs instanceof Task && i < taskArgs.task_array.length - 1) throw new FlowTaskError(taskName, "SubFlows must be at the last index."); | ||
if(!tasks.hasOwnProperty(subFlow.dataName) && (!data || !data.hasOwnProperty(subFlow.dataName)) || subFlow.dataName == taskName) throw new FlowTaskError(taskName, "SubFlow data '" + subFlow.dataName + "' does not exist. Provide the name of a task or data from the parent flow. Possible values include: " + Object.keys(tasks).concat(Object.keys(data)).filter(function(name){ return name != taskName }).join(', ')); | ||
fn = { receiverName: subFlow.dataName, tasks: subFlow.tasks }; | ||
var prereqs = scanForPrereqs(subFlow.tasks, us.extend({}, data, tasks)); | ||
autoTask.push(subFlow.dataName); | ||
autoTask = autoTask.concat(Object.keys(prereqs)); | ||
Flow.prototype.execute = function execute(data, callback) { | ||
if(arguments.length == 1) { | ||
callback = data, data = {}; | ||
} | ||
if(taskArgs instanceof SubFlow) { | ||
interpretSubFlow(taskArgs); | ||
} else if(typeof taskArgs == 'function') { | ||
fn = taskArgs; | ||
} else if(task) { | ||
task.name = taskName; | ||
task.workflow = self; | ||
task.task_requirements = []; | ||
task.data_requirements = []; | ||
// task.context_requirements = []; | ||
for(var i in task.task_array) { | ||
var taskArg = task.task_array[i]; | ||
if(typeof taskArg == 'function') { | ||
if(fn) throw new FlowTaskError(taskName, "More than one function specified (at index " + fnIndex + " and " + i + ")."); | ||
fn = taskArg; | ||
fnIndex = i; | ||
} else if(taskArg instanceof SubFlow) { | ||
interpretSubFlow(taskArg); | ||
fnIndex = i; | ||
} else if(typeof taskArg == 'string') { | ||
var taskArgParts = taskArg.split('.'); | ||
taskArg = taskArgParts[0]; | ||
var auto_task_type = tasks.hasOwnProperty(taskArg) && 'task' || data && data.hasOwnProperty(taskArg) && 'data' || context && context.hasOwnProperty(taskArg) && typeof context[taskArg] != 'function' && 'context'; | ||
if(taskArg == taskName) auto_task_type = null; | ||
if(auto_task_type) { | ||
if(fn) args.push(taskArgParts); //there's already a fn, so assume that 'foo.bar.baz' must be a parameter to fn. | ||
else if(taskArgParts.length > 1) { | ||
fn = { name: taskArgParts.pop(), receiverName: taskArgParts }; //there's no fn yet, so assume that 'foo.bar.baz' means baz must be a function to execute on foo.bar | ||
fnIndex = i; | ||
} | ||
if(auto_task_type == 'task') task.task_requirements.push(taskArg); | ||
else if(auto_task_type == 'data' || auto_task_type == 'context') task.data_requirements.push(taskArg); | ||
//else if(auto_task_type == 'context') task.context_requirements.push(taskArg); | ||
autoTask.push(taskArg); | ||
} else throw new FlowTaskError(taskName, "Unknown string '" + taskArg + "' must be either the name of a task or the name of data"); | ||
} else throw new FlowTaskError(taskName, "Unknown symbol at index '" + i + "' must be either the name of a task, the name of data, or be the name of a function on the result of a task or data"); | ||
} | ||
if(!fn) throw new FlowTaskError(taskName, "Function required."); | ||
} else { | ||
throw new FlowTaskError(taskName, "Invalid flow type. Must be function, subFlow, or array."); | ||
} | ||
return { fn: fn, args: args, autoTask: autoTask, task: task, isSync: (task instanceof SyncTask) }; | ||
if(!callback) throw new errors.ArgumentNull('callback'); | ||
return Execution.launch(this, data, callback); | ||
} | ||
Workflow.prototype.executeTaskPlan = function executeTaskPlan(taskPlan, taskName){ | ||
Flow.prototype._createPlan = function _createPlan(data) { | ||
if(this._plan) return this._plan; | ||
var self = this; | ||
var fn = taskPlan.fn; | ||
var args = taskPlan.args; | ||
var autoTask = taskPlan.autoTask; | ||
var task = taskPlan.task; | ||
var plan = { auto: {}, auto_task: undefined, prereqs: {} }; | ||
if(typeof fn == 'object' && fn.tasks) { //subflows | ||
autoTask.push(function(cb, results){ | ||
var rawResultData = results[fn.receiverName]; | ||
if(rawResultData === null || rawResultData === undefined) { | ||
throw new FlowTaskError(taskName, "Result of '" + fn.receiverName + "' returned no data. Could not start SubFlow."); | ||
} else if(Array.isArray(rawResultData)) { | ||
var newData = rawResultData.map(function(dataItem){ | ||
var newDataItem = us.extend({}, results); | ||
newDataItem[fn.receiverName] = dataItem; | ||
return newDataItem; | ||
}); | ||
} else { | ||
var newData = us.extend({}, results); | ||
newData[fn.receiverName] = rawResultData; | ||
} | ||
var sub_workflow = new Workflow(newData, fn.tasks, cb); | ||
sub_workflow.parent = self; | ||
sub_workflow.execute(fn.receiverName); | ||
}); | ||
} else { | ||
autoTask.push(function(callback, results){ | ||
var cb = function(err, result){ | ||
if(err && err.name == "ArgumentNullError" && self.tasks[err.argumentName]) err.message = "Not Found: " + self.tasks[err.argumentName].toString(results) | ||
if(err) return callback(err); | ||
if(arguments.length > 2) result = Array.prototype.slice.call(arguments, 1); | ||
if(task) return Task.complete.call(task, result, results, callback); | ||
else callback(null, result); | ||
}; | ||
this._initialize(); | ||
var fnArgs; | ||
try { | ||
//we will generate the list of args to apply to the fn. | ||
fnArgs = args.map(function(arg){ | ||
return evalMessages(results, arg, taskName); | ||
}); | ||
} catch(e) { | ||
if(e.name == "FlowTaskArgumentNullError") { | ||
e = new errors.ArgumentNull(e.argumentName); | ||
e.message = "Not Found: " + self.tasks[taskName].toString(results, e.argumentName); | ||
var planTasks = function(o){ | ||
var keys = Object.keys(o); | ||
keys.forEach(function(item_name){ | ||
var item = o[item_name]; | ||
if(item._createPlan) { | ||
var item_plan = (item instanceof Flow) ? item._createPlan(us.extend({}, data, us.omit(self.tasks, item.name))) : item._createPlan(data); | ||
var item_prereqs = item_plan.auto_task.slice(0, item_plan.auto_task.length - 1); | ||
for(var i=0; i<item_prereqs.length; i++) { | ||
var item_prereq = item_prereqs[i]; | ||
var is_context = !(data && data.hasOwnProperty(item_prereq) || self.tasks.hasOwnProperty(item_prereq) || self.flows.hasOwnProperty(item_prereq)); | ||
if(data && data.hasOwnProperty(item_prereq)) plan.prereqs[item_prereq] = true; | ||
if(is_context && self.context_name && item_prereq != self.context_name) { | ||
plan.auto[item_prereq] = plan.auto[item_prereq] || (function(item_prereq){ | ||
return [self.context_name, function(cb, results){ | ||
var context = results[self.context_name]; | ||
if(!context) return cb(new errors.ArgumentNull("Flow context \"" + self.context_name + "\"")); | ||
cb(null, context[item_prereq]); | ||
}]; | ||
})(item_prereq); | ||
} | ||
} | ||
return callback(e); | ||
} | ||
if(!taskPlan.isSync) fnArgs.push(cb); | ||
plan.auto[item_name] = item_plan.auto_task; | ||
} | ||
}); | ||
}; | ||
var receiver; | ||
if(typeof fn != 'function') { | ||
var fnName = fn.name; | ||
var receiverName = fn.receiverName; | ||
try { | ||
receiver = evalMessages(results, receiverName, taskName); | ||
} catch(e) { | ||
if(e.name == "FlowTaskArgumentNullError") e.message = "Not Found: " + self.tasks[taskName].toString(results, e.argumentName); | ||
return callback(e); | ||
} | ||
if(!receiver) { | ||
var err = new errors.ArgumentNull(receiverName.join('.')); | ||
if(self.tasks[receiverName[0]]) err.message = "Not Found: " + self.tasks[receiverName[0]].toString(results, err.argumentName); | ||
return callback(err) | ||
} | ||
fn = receiver[fnName]; | ||
if(!fn) throw new FlowTaskError(taskName, "Unknown symbol '" + fnName + "' must be either the name of a task, the name of data, or the name of a function on '" + receiverName + "'"); | ||
} | ||
if(!Object.keys(self.tasks).length && !Object.keys(self.flows).length) throw Error("No tasks given for Flow."); | ||
try { | ||
var return_value = fn.apply(receiver, fnArgs); | ||
if(taskPlan.isSync) cb(null, return_value); | ||
} catch(e){ | ||
throw new FlowTaskError(taskName, "Error during execution of function.", e); | ||
} | ||
}); | ||
if(self.tasks) planTasks(self.tasks); | ||
if(self.flows) planTasks(self.flows); | ||
if(self.context_name) { | ||
var context_prereq = self.context_name.split('.')[0]; | ||
if(!(data && data.hasOwnProperty(context_prereq))) { | ||
throw new FlowTaskError(self.name, "Subflow data '" + context_prereq + "' does not exist. Provide the name of a flow, task or data from the parent flow. Possible values include: " + Object.keys(data).join(', ')); | ||
} | ||
plan.prereqs[context_prereq] = true; | ||
} | ||
return autoTask; | ||
} | ||
plan.auto_task = Object.keys(plan.prereqs); | ||
Array.prototype.push.apply(plan.auto_task, this._explicit_reqs); | ||
plan.auto_task.push(function(cb, results){ | ||
var data; | ||
var context = self.parent && self.context_name ? results[self.context_name] : null; | ||
if(context === null || context === undefined) { | ||
return cb(new FlowTaskError(self.name, "Result of '" + self.context_name + "' returned no data. Could not start SubFlow.")); | ||
} else if(Array.isArray(context)){ | ||
data = context.map(function(context_item){ | ||
var data_item = us.extend({}, results); | ||
data_item[self.context_name] = context_item; | ||
return data_item; | ||
}); | ||
} else { | ||
data = us.extend({}, results); | ||
data[self.context_name] = context; | ||
} | ||
Execution.launch(self, data, cb); | ||
}); | ||
var evalMessages = function evalMessages(receiver, messages, taskName){ | ||
if(Array.isArray(messages)) { | ||
for(var i in messages) { | ||
if(receiver === undefined || receiver === null) { | ||
var receiverName = messages.slice(1,i).join('.'); | ||
var err = new FlowTaskArgumentNullError(taskName, messages[i-1], messages[i]); | ||
err.argumentName = receiverName; | ||
throw err; | ||
} | ||
receiver = receiver[messages[i]]; | ||
} | ||
return receiver; | ||
} else return receiver[messages]; | ||
return this._plan = plan; | ||
} | ||
var scanForPrereqs = function scanForPrereqs(tasks, allTasks) { | ||
var prereqs = {}; | ||
for(var taskName in tasks) { | ||
var taskArgs = tasks[taskName]; | ||
if(taskArgs instanceof SubFlow){ | ||
us.extend(prereqs, scanForPrereqs(taskArgs.tasks, us.extend({}, allTasks, tasks))); | ||
} else if(taskArgs instanceof Task){ | ||
for(var i in taskArgs.task_array) { | ||
var taskArg = taskArgs.task_array[i]; | ||
if(taskArg instanceof SubFlow) { | ||
us.extend(prereqs, scanForPrereqs(taskArg.tasks, us.extend({}, allTasks, tasks))); | ||
} else if(typeof taskArg == 'function') { | ||
} else if(typeof taskArg == 'string') { | ||
taskArg = taskArg.split('.')[0]; | ||
if(allTasks.hasOwnProperty(taskArg)) { | ||
prereqs[taskArg] = true; | ||
} | ||
} else { | ||
} | ||
} | ||
Flow.prototype._initialize_tasks = function _initialize_tasks() { | ||
var task_names = Object.keys(this.tasks); | ||
for(var i=0; i<task_names.length; i++) { | ||
var task_name = task_names[i]; | ||
var task = this.tasks[task_name]; | ||
if(task instanceof Task) { | ||
task.flow = this; | ||
} else if(task instanceof Flow) { | ||
task.parent = this; | ||
this.flows[task_name] = task; | ||
} else { | ||
throw new TypeError('task ' + task_name + ' must be a Task or Flow'); | ||
} | ||
if(task.name && task.name != task_name) throw new Error("Task " + task_name + " already named " + task.name); | ||
task.name = task_name; | ||
} | ||
return prereqs; | ||
} | ||
var SubFlow = function(dataName, tasks){ | ||
this.dataName = dataName; | ||
this.tasks = tasks; | ||
if(!dataName) throw new Error("SubFlow error: No data given for subFlow. Provide the name of a task or data from the parent flow."); | ||
if(!tasks) throw new Error("SubFlow error: No tasks given for subFlow."); | ||
if(typeof tasks == 'object') { | ||
for(var name in tasks) if(Array.isArray(tasks[name])) tasks[name] = new AsyncTask(tasks[name]); | ||
} | ||
}; | ||
module.exports.flow.subFlow = function(dataName, tasks) { | ||
return new SubFlow(dataName, tasks); | ||
}; | ||
module.exports.flow.asyncTask = function(task_array) { | ||
if(!(task_array instanceof Array)) task_array = us.values(arguments); | ||
return new AsyncTask(task_array); | ||
} | ||
module.exports.flow.syncTask = function(task_array) { | ||
if(!(task_array instanceof Array)) task_array = us.values(arguments); | ||
return new SyncTask(task_array); | ||
} | ||
Flow.prototype.requires = Task.prototype.requires; |
@@ -6,3 +6,3 @@ { | ||
"author": "David Fenster <david@dfenster.com>", | ||
"version": "0.2.2", | ||
"version": "0.3.0", | ||
"repository": { | ||
@@ -9,0 +9,0 @@ "type": "git", |
224
README.md
@@ -1,130 +0,172 @@ | ||
fnFlow | ||
fnflow | ||
====== | ||
Pronounced "effin' flow" because it's so badass, fnFlow is a Javascript control flow library heavily influenced by Caolan McMahon's async that encourages a proper functional design pattern. | ||
Pronounced "effin' flow" because it's so badass, fnflow is a Javascript control flow library heavily influenced by Caolan McMahon's async that encourages a proper functional design pattern. | ||
### flow([data], tasks, [callback]) | ||
For a complicated series of both asynchronous and synchronous tasks, using the Flow class makes adding new tasks much easier and makes the code more readable. It also encourages you to define functions in places where they can be reused more easily. This makes it an excellent choice for design patterns like MVC where it is a goal to strive for "fat model, skinny controller." | ||
Like [async.auto](https://github.com/caolan/async#auto), it determines the best order for running functions based on their requirements. | ||
__Basic Usage__ | ||
For a complicated series of async tasks, using the flow function makes adding | ||
new tasks much easier and makes the code more readable. It also encourages you to define functions in places where they can be reused more easily. This makes it an excellent choice for design patterns like MVC where it is a goal to strive for "fat model, skinny controller." | ||
```javascript | ||
var Flow = require('fnflow').Flow, Task = Flow.Task; | ||
Each function can optionally depend on other functions being completed first, | ||
and each function is run as soon as its requirements are satisfied. If any of | ||
the functions pass an error to their callback, that function will not complete | ||
(so any other functions depending on it will not run) and the main callback | ||
will be called immediately with the error. The main callback receives an object | ||
containing the results of functions which have completed so far. | ||
var flow = new Flow({ | ||
author: new Task(Author.getByName, 'author_name'), | ||
genre: new Task(Genre.getByName, 'genre_name'), | ||
books: new Task(Book.findByAuthorAndGenre, 'author', 'genre'), | ||
}); | ||
Note, all functions are assumed to expect a callback as the final argument, so it is unsafe to pass functions in the tasks object which cannot handle the | ||
extra argument. | ||
flow.execute({ author_name: 'Brandon Sanderson', genre_name: 'Fantasy' }, function(err, results){ | ||
if(err) return console.error(err); | ||
console.log(results.books.length + " books found."); | ||
}); | ||
``` | ||
...which translates to the following workflow: | ||
* Get the author by name "Brandon Sanderson" and get the genre by name "Fantasy" asynchronously, in parallel. | ||
* Get the fantasy books in the Wheel of Time series written by Brandon Sanderson by calling Book.findByAuthorAndGenre(author, genre, callback) by using the results of the author and genre tasks. | ||
## Flow(tasks) | ||
Constructor function. It determines the best order for running functions based on their requirements. Flow automatically determines which functions depend on other functions to complete first by examining all function arguments, and each function is run as soon as its requirements are satisfied. If any of the functions pass an error to their callback, that function will not complete (so any other functions depending on it will not run) and the main callback will be called immediately with the error. The main callback receives an object containing the results of functions which have completed so far. | ||
__Arguments__ | ||
* data (optional) - Either an object literal containing a set of static data, or an array of such objects. The key used for each data value is used when specifying parameters in tasks. If an array is specified, the tasks will execute in parallel for each data item in the array, and the callback will be passed an array of results. | ||
* tasks - An object literal containing named functions or named arrays of parameters or requirements, with the function itself somewhere in the array. Specify requirements to the left of the function and parameters to the right. The key used for each function or array is used when specifying parameters or requirements to other tasks. When called, the task function receives the results of the named parameters as arguments as well as a final callback(err, result) argument which must be called when finished, passing an error (which can be null) and the result of the function's execution. The task function may optionally be the name of a function to perform on the result of the last named requirement (the item directly to the left). | ||
* callback(err, results) (optional) - An optional callback which is called when all the | ||
tasks have been completed. The callback will receive an error as an argument | ||
if any tasks pass an error to their callback. Results will always be passed | ||
but if an error occurred, no other tasks will be performed, and the results | ||
object will only contain partial results. | ||
* tasks - An object literal containing named Tasks. The key used for each function or array is used when specifying parameters or requirements to other tasks. | ||
__Example__ | ||
### flow.execute(data, callback) | ||
* data - Either an object literal containing a set of static data, or an array of such objects. The key used for each data value is used when specifying parameters in tasks. If an array is specified, the tasks will execute in parallel for each data item in the array, and the callback will be passed an array of results. | ||
* callback(err, results) - A callback which is called when all the tasks have been completed, | ||
or an error has ocurred. Results will always be passed but if an error occurred, | ||
no other tasks will be performed, and the results object will only contain partial results. | ||
__Running Multple Sets of Tasks in Parallel__ | ||
```js | ||
fnFlow.flow({ | ||
authorName: 'Brandon Sanderson', | ||
genreName: 'Fantasy', | ||
bookSeriesName: 'The Wheel of Time' | ||
}, { | ||
getAuthor: [Author.getByName, 'authorName'], | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBookSeries: [BookSeries.getByName, 'bookSeriesName'], | ||
getBooks: [Book.findBooksByAuthorGenreAndBookSeries, 'getAuthor', 'getGenre', 'getBookSeries'] | ||
var flow = new Flow({ | ||
author: new Task(Author.getByName, 'author_name'), | ||
books: new Task('author.getBooks'), | ||
}); | ||
flow.execute([ | ||
{ author_name: 'Brandon Sanderson' }, | ||
{ author_name: 'Jack Vance' } | ||
], function(err, results) { | ||
if(err) return console.error(err); | ||
results.forEach(function(result){ console.log(result.author_name + " wrote " + result.books.length); }); | ||
}); | ||
``` | ||
Which translates to the following workflow: | ||
* Get the author by name "Brandon Sanderson", get the genre by name "Fantasy", and get the book series by name "The Wheel of Time" in parallel. | ||
* Get the fantasy books in the Wheel of Time series written by Brandon Sanderson by calling Book.findBooksByAuthorGenreAndBookSeries(author, genre, bookSeries, callback) | ||
...which translates to the following workflow: | ||
To do this using async.auto would look like this: | ||
* At the same time, asynchronously do the following: | ||
* Get the author by name "Brandon Sanderson," and then retrieve his books. | ||
* Get the author by name "Jack Vance," and then retrieve his books. | ||
## Flow(name_of_result, tasks) | ||
Constructor function. | ||
Use this constructor for nesting Flows. | ||
Nesting Flows is useful when you want to execute a set of tasks against each item in an Array result of a parent Flow. | ||
__Arguments__ | ||
* name_of_result - A string that identifies the name of a result from a parent Flow. The value of the result will be used to execute this flow. | ||
* tasks - An object literal containing named Tasks. The key used for each function or array is used when specifying parameters or requirements to other tasks. | ||
__Nested Flow Example__ | ||
```js | ||
async.auto({ | ||
getAuthor: function(callback, results){ | ||
Author.getByName('Brandon Sanderson', callback); | ||
}, | ||
getGenre: function(callback, results){ | ||
Genre.getByName('Fantasy', callback); | ||
}, | ||
getBookSeries: function(callback, results){ | ||
BookSeries.getByName('The Wheel of Time', callback); | ||
}, | ||
getBooks: ['getAuthor', 'getGenre', 'getBookSeries', function(callback, results){ | ||
Book.findBooksByAuthorGenreAndBookSeries(results.getAuthor, results.getGenre, results.getBookSeries, callback); | ||
}] | ||
var flow = new Flow({ | ||
author: new Task(Author.getByName, 'author_name'), | ||
books: new Task('author.getBooks'), | ||
books_data: new Flow('books', { | ||
pages: new Task('books.getPages'), | ||
words: new Task('books.getWords') | ||
}) | ||
}); | ||
flow.execute({ author_name: 'Brandon Sanderson' }, function(err, results) { | ||
if(err) return console.error(err); | ||
var total_pages = 0, total_words = 0; | ||
results.books_data.forEach(function(book_data){ | ||
total_pages += book_data.pages; | ||
}); | ||
console.log(results.author_name + " wrote " + total_pages + " pages and " + total_words + " words."); | ||
}); | ||
``` | ||
...which translates to the following workflow: | ||
__Another Example__ | ||
* Get the author by name "Brandon Sanderson" | ||
* Get his books. | ||
* For each book, asynchronously in parallel, get the total number of pages and words at the same time. | ||
## Task(fn_or_string, [*string_args]) | ||
Constructor function. Represents an asynchronous function whose final argument is a callback function(err, value). | ||
* fn_or_string - An actual function object, or a string that describes a function that is an attribute of another task result. | ||
* string_args - Each one represents an argument value to pass to the fn_or_string. Each string must stem from a piece of given to the Flow.prototype.execute function, or a task name. | ||
### task.requires(task_names) | ||
Explicitly specify the names of tasks that must complete prior to the execution of this task. This is useful when a requisite task does not yield a value that is meaningful to this task. | ||
__Example__ | ||
```js | ||
fnFlow.flow({ | ||
authorName: 'Brandon Sanderson', | ||
genreName: 'Fantasy' | ||
}, { | ||
getAuthor: [Author.getByName, 'authorName'], | ||
getGenre: [Genre.getByName, 'genreName'], | ||
assertGenreExistence: [Genre.assertExistence, 'getGenre'], | ||
getBooks: ['assertGenreExistence', 'getGenre.findBooksByAuthor', 'getAuthor.id'] | ||
}, function(err, results) { | ||
if(err) return console.error(err); //genre probably didn't exist. | ||
console.log('Number of books:', results.getBooks.length); | ||
var flow = new Flow({ | ||
user: new Task(User.getByUsername, 'username'), | ||
current_user: new Task(User.getByUsername, 'current_username') | ||
checkAuthorization: new Task(user.checkAuthorization, 'current_user') | ||
updateUser: new Task(user.update, 'user_data').requires('checkAuthorization'), | ||
}); | ||
flow.execute({ | ||
username: "jsmith", | ||
current_username: "bjoe", | ||
user_data: { email: 'bjoe@shutterstock.com' } | ||
}, function(err, results){ | ||
if(err) return console.error(err); | ||
console.log(results.books.length + " books found."); | ||
}); | ||
``` | ||
Which translates to the following workflow: | ||
* Get the author by name "Brandon Sanderson", and get the genre by name "Fantasy" in parallel. | ||
* Right after getting the genre, assert that the genre exists and interrupt the workflow if it doesn't. | ||
* Get the fantasy books written by Brandon Sanderson by calling genre.findBooksByAuthor(author, callback) | ||
* Print the error if there was one, and log the number of books retrieved if not. | ||
...which translates to the following workflow: | ||
* Get the user 'jsmith' and the current user 'bjoe' asynchronously in parallel at the same time. | ||
* Check whether user 'bjoe' is authorized to update the email address of user 'jsmith' | ||
* Once the authorization check is complete, update the user 'jsmith'. | ||
__Running Multple Sets of Tasks in Parallel__ | ||
### task.assertExists(task_names) | ||
Exit the Flow exeuction with an error if the result of the task does not yield a value. | ||
__Example__ | ||
```js | ||
fnFlow.flow([ | ||
{ authorName: 'Brandon Sanderson', | ||
genreName: 'Fantasy' | ||
}, | ||
{ authorName: 'Jack Vance', | ||
genreName: 'Fantasy' | ||
} | ||
], { | ||
getAuthor: [Author.getByName, 'authorName'], | ||
getGenre: [Genre.getByName, 'genreName'], | ||
assertGenreExistence: [Genre.assertExistence, 'getGenre'], | ||
getBooks: ['assertGenreExistence', 'getGenre.findBooksByAuthor', 'getAuthor.id'] | ||
}, function(err, results) { | ||
if(err) return console.error(err); //genre probably didn't exist. | ||
results.forEach(function (result) { | ||
console.log('Number of books for ' + result.getAuthor.name + ':', result.getBooks.length); | ||
}); | ||
var flow = new Flow({ | ||
author: new Task(Author.getByName, 'author_name').assertExists(), | ||
books: new Task(Book.findByAuthor, 'author'), | ||
}); | ||
flow.execute({ author_name: 'Brandon Sanderson' }, function(err, results){ | ||
if(err) return console.error(err); //this will be true if there was no author found | ||
console.log(results.books.length + " books found."); | ||
}); | ||
``` | ||
This does the exact same thing as the above example, but does it once for Brandon Sanderson and once for Jack Vance, in parallel. | ||
...which translates to the following workflow: | ||
* Get the author asynchronously by name. If no author is found, exit with an error. | ||
* If an author was found, asynchronously retrieve the books written by the author. | ||
### flow.subFlow(dataName, tasks) | ||
This function pairs with the original _flow_ function to execute a sub flow within a parent. Given the name of a task or data from the parent flow, | ||
and a set of new tasks, it will execute the equivalent of one flow call from within another. It is most handy when you must invoke operations on each | ||
item in an array of results from a previous task in the flow, though, it will work on a single result as well. | ||
## Authors | ||
This library was developed by David Fenster at [Shutterstock](http://www.shutterstock.com) | ||
This library was developed by [David Fenster](https://github.com/dfenster) with major contributions from [Ben Kovacevich](https://github.com/bkovacevich) at [Shutterstock](http://www.shutterstock.com) | ||
@@ -131,0 +173,0 @@ |
@@ -1,4 +0,4 @@ | ||
var flow = require('../lib/fnFlow').flow; | ||
var us = require('underscore'); | ||
var test_data = require('../support/test-data'); | ||
var Flow = require('../lib/fnFlow').Flow, Task = Flow.Task, Fn = Flow.Fn; | ||
@@ -18,6 +18,6 @@ Book = test_data.Book; | ||
module.exports["flow data"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getBook: new Task(Book.getById, 'bookId') | ||
}).execute({ | ||
bookId: 1 | ||
}, { | ||
getBook: [Book.getById, 'bookId'] | ||
}, function(err, results){ | ||
@@ -30,7 +30,7 @@ test.equals(results.getBook, Book.all[1]); | ||
module.exports["flow functions and data"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getAuthor: new Task(Author.getById, 'authorId'), | ||
getBooks: new Task(Book.findByAuthorId, 'getAuthor') | ||
}).execute({ | ||
authorId: 5 | ||
}, { | ||
getAuthor: [Author.getById, 'authorId'], | ||
getBooks: [Book.findByAuthorId, 'getAuthor'] | ||
}, function(err, results){ | ||
@@ -48,9 +48,9 @@ test.ok(!err); | ||
module.exports["parallel flow functions and data"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getAuthor: new Task(Author.getByName, 'authorName'), | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooks: new Task(Book.findByGenreAndAuthor, 'getGenre', 'getAuthor'), | ||
}).execute({ | ||
authorName: 'Dan Brown', | ||
genreName: 'Fiction' | ||
}, { | ||
getAuthor: [Author.getByName, 'authorName'], | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooks: [Book.findByGenreAndAuthor, 'getGenre', 'getAuthor'] | ||
}, function(err, results){ | ||
@@ -70,6 +70,5 @@ test.ok(!err); | ||
module.exports["function as task"] = function(test){ | ||
flow({ | ||
}, { | ||
getAuthors: Author.getAll | ||
}, function(err, results){ | ||
new Flow({ | ||
getAuthors: new Task(Author.getAll) | ||
}).execute(function(err, results){ | ||
test.ok(!err, "no error"); | ||
@@ -84,9 +83,9 @@ test.deepEqual(results, { | ||
module.exports["flow task callback with error"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getAuthor: new Task(Author.getByName, 'authorName'), | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooks: new Task(Book.findByGenreAndAuthor, 'getGenre', 'getAuthor') | ||
}).execute({ | ||
authorName: 'Dan Brown', | ||
genreName: '???' | ||
}, { | ||
getAuthor: [Author.getByName, 'authorName'], | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooks: [Book.findByGenreAndAuthor, 'getGenre', 'getAuthor'] | ||
}, function(err, results){ | ||
@@ -100,9 +99,9 @@ test.ok(err); | ||
module.exports["instance task execution"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getAuthor: new Task(Author.getByName, 'authorName'), | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooks: new Task('getGenre.findBooksByAuthor', 'getAuthor') | ||
}).execute({ | ||
authorName: 'Dan Brown', | ||
genreName: 'Fiction' | ||
}, { | ||
getAuthor: [Author.getByName, 'authorName'], | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooks: ['getGenre.findBooksByAuthor', 'getAuthor'] | ||
}, function(err, results){ | ||
@@ -122,10 +121,10 @@ test.ok(!err); | ||
module.exports["prerequisite task execution"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getAuthor: new Task(Author.getByName, 'authorName'), | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
assertGenreExistence: new Task(Genre.assertExistence, 'getGenre'), | ||
getBooks: new Task(Book.findByGenreAndAuthor, 'getGenre', 'getAuthor').requires('assertGenreExistence') | ||
}).execute({ | ||
authorName: 'Dan Brown', | ||
genreName: 'Fiction' | ||
}, { | ||
getAuthor: [Author.getByName, 'authorName'], | ||
getGenre: [Genre.getByName, 'genreName'], | ||
assertGenreExistence: [Genre.assertExistence, 'getGenre'], | ||
getBooks: ['assertGenreExistence', Book.findByGenreAndAuthor, 'getGenre', 'getAuthor'] | ||
}, function(err, results){ | ||
@@ -145,12 +144,11 @@ test.ok(!err); | ||
module.exports["prerequisite task execution with short circuit error"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getAuthor: new Task(Author.getByName, 'authorName'), | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
assertGenreExistence: new Task(Genre.assertExistence, 'getGenre'), | ||
getBooks: new Task(Book.findByGenreAndAuthor, 'getGenre', 'getAuthor').requires('assertGenreExistence') | ||
}).execute({ | ||
authorName: 'Dan Brown', | ||
genreName: 'Yourmom' | ||
}, { | ||
getAuthor: [Author.getByName, 'authorName'], | ||
getGenre: [Genre.getByName, 'genreName'], | ||
assertGenreExistence: [Genre.assertExistence, 'getGenre'], | ||
getBooks: ['assertGenreExistence', Book.findByGenreAndAuthor, 'getGenre', 'getAuthor'] | ||
}, function(err, results){ | ||
@@ -170,12 +168,11 @@ test.ok(err); | ||
module.exports["prerequisite instance task execution"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getAuthor: new Task(Author.getByName, 'authorName'), | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
assertGenreExistence: new Task(Genre.assertExistence, 'getGenre'), | ||
getBooks: new Task('getGenre.findBooksByAuthor', 'getAuthor').requires('assertGenreExistence'), | ||
}).execute({ | ||
authorName: 'Dan Brown', | ||
genreName: 'Fiction' | ||
}, { | ||
getAuthor: [Author.getByName, 'authorName'], | ||
getGenre: [Genre.getByName, 'genreName'], | ||
assertGenreExistence: [Genre.assertExistence, 'getGenre'], | ||
getBooks: ['assertGenreExistence', 'getGenre.findBooksByAuthor', 'getAuthor'] | ||
}, function(err, results){ | ||
@@ -195,12 +192,11 @@ test.ok(!err); | ||
module.exports["prerequisite instance task execution with short circuit error"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getAuthor: new Task(Author.getByName, 'authorName'), | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
assertGenreExistence: new Task(Genre.assertExistence, 'getGenre'), | ||
getBooks: new Task('getGenre.findBooksByAuthor', 'getAuthor').requires('assertGenreExistence'), | ||
}).execute({ | ||
authorName: 'Dan Brown', | ||
genreName: 'Yourmom' | ||
}, { | ||
getAuthor: [Author.getByName, 'authorName'], | ||
getGenre: [Genre.getByName, 'genreName'], | ||
assertGenreExistence: [Genre.assertExistence, 'getGenre'], | ||
getBooks: ['assertGenreExistence', 'getGenre.findBooksByAuthor', 'getAuthor'] | ||
}, function(err, results){ | ||
@@ -221,3 +217,5 @@ test.ok(err); | ||
module.exports["multiple instance parameter"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getGenre: new Task(Genre.getById, 'page.chapter.book.genreId'), | ||
}).execute({ | ||
page: { | ||
@@ -230,4 +228,2 @@ number: 1, | ||
} | ||
}, { | ||
getGenre: [Genre.getById, 'page.chapter.book.genreId'] | ||
}, function(err, results){ | ||
@@ -240,3 +236,5 @@ test.deepEqual(results.getGenre, Genre.all[4]); | ||
module.exports["result multi instance function"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getAuthor: new Task('page.chapter.book.getAuthor'), | ||
}).execute({ | ||
page: { | ||
@@ -249,4 +247,2 @@ number: 1, | ||
} | ||
}, { | ||
getAuthor: ['page.chapter.book.getAuthor'] | ||
}, function(err, results){ | ||
@@ -259,9 +255,9 @@ test.deepEqual(results.getAuthor, Author.all[3]); | ||
module.exports["instance task execution with result instance parameter"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getAuthor: new Task(Author.getByName, 'authorName'), | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooks: new Task('getGenre.findBooksByAuthor', 'getAuthor.id'), | ||
}).execute({ | ||
authorName: 'Dan Brown', | ||
genreName: 'Fiction' | ||
}, { | ||
getAuthor: [Author.getByName, 'authorName'], | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooks: ['getGenre.findBooksByAuthor', 'getAuthor.id'] | ||
}, function(err, results){ | ||
@@ -281,3 +277,8 @@ test.ok(!err); | ||
module.exports["multiple asyncronus tasks with prerequisite task execution"] = function(test){ | ||
flow( [ | ||
new Flow({ | ||
getAuthor: new Task(Author.getByName, 'authorName'), | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
assertGenreExistence: new Task(Genre.assertExistence, 'getGenre'), | ||
getBooks: new Task(Book.findByGenreAndAuthor, 'getGenre', 'getAuthor').requires('assertGenreExistence'), | ||
}).execute([ | ||
{ authorName: 'Dan Brown', | ||
@@ -289,8 +290,3 @@ genreName: 'Fiction' | ||
} | ||
], { | ||
getAuthor: [Author.getByName, 'authorName'], | ||
getGenre: [Genre.getByName, 'genreName'], | ||
assertGenreExistence: [Genre.assertExistence, 'getGenre'], | ||
getBooks: ['assertGenreExistence', Book.findByGenreAndAuthor, 'getGenre', 'getAuthor'] | ||
}, function(err, results){ | ||
], function(err, results){ | ||
test.ok(!err); | ||
@@ -316,3 +312,8 @@ test.deepEqual(results, [ | ||
module.exports["multiple asyncronus tasks with prerequisite task execution (error)"] = function(test){ | ||
flow([ | ||
new Flow({ | ||
getAuthor: new Task(Author.getByName, 'authorName'), | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
assertGenreExistence: new Task(Genre.assertExistence, 'getGenre'), | ||
getBooks: new Task(Book.findByGenreAndAuthor, 'getGenre', 'getAuthor').requires('assertGenreExistence'), | ||
}).execute([ | ||
{ authorName: 'Dan Brown', | ||
@@ -324,8 +325,3 @@ genreName: 'Fiction' | ||
} | ||
], { | ||
getAuthor: [Author.getByName, 'authorName'], | ||
getGenre: [Genre.getByName, 'genreName'], | ||
assertGenreExistence: [Genre.assertExistence, 'getGenre'], | ||
getBooks: ['assertGenreExistence', Book.findByGenreAndAuthor, 'getGenre', 'getAuthor'] | ||
}, function(err, results){ | ||
], function(err, results){ | ||
test.ok(err); | ||
@@ -345,3 +341,8 @@ test.equal(results[0], undefined); | ||
module.exports["multiple asyncronus tasks with prerequisite instance task execution"] = function(test){ | ||
flow([ | ||
new Flow({ | ||
getAuthor: new Task(Author.getByName, 'authorName'), | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
assertGenreExistence: new Task(Genre.assertExistence, 'getGenre'), | ||
getBooks: new Task('getGenre.findBooksByAuthor', 'getAuthor').requires('assertGenreExistence'), | ||
}).execute([ | ||
{ authorName: 'Dan Brown', | ||
@@ -353,8 +354,3 @@ genreName: 'Fiction' | ||
} | ||
], { | ||
getAuthor: [Author.getByName, 'authorName'], | ||
getGenre: [Genre.getByName, 'genreName'], | ||
assertGenreExistence: [Genre.assertExistence, 'getGenre'], | ||
getBooks: ['assertGenreExistence', 'getGenre.findBooksByAuthor', 'getAuthor'] | ||
}, function(err, results){ | ||
], function(err, results){ | ||
test.ok(!err); | ||
@@ -379,5 +375,9 @@ test.deepEqual(results, [ | ||
module.exports["multiple asyncronus tasks with prerequisite instance task execution (error)"] = function(test){ | ||
flow([ | ||
new Flow({ | ||
getAuthor: new Task(Author.getByName, 'authorName'), | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
assertGenreExistence: new Task(Genre.assertExistence, 'getGenre'), | ||
getBooks: new Task('getGenre.findBooksByAuthor', 'getAuthor').requires('assertGenreExistence'), | ||
}).execute([ | ||
{ authorName: 'Dan Brown', | ||
@@ -389,8 +389,3 @@ genreName: 'Fiction' | ||
} | ||
], { | ||
getAuthor: [Author.getByName, 'authorName'], | ||
getGenre: [Genre.getByName, 'genreName'], | ||
assertGenreExistence: [Genre.assertExistence, 'getGenre'], | ||
getBooks: ['assertGenreExistence', 'getGenre.findBooksByAuthor', 'getAuthor'] | ||
}, function(err, results){ | ||
], function(err, results){ | ||
test.ok(err); | ||
@@ -409,10 +404,8 @@ test.equal(results[0], undefined); | ||
module.exports["task name same as instance method"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooks: new Task('getGenre.getBooks'), | ||
}).execute({ | ||
genreName: 'Fantasy' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooks: ['getGenre.getBooks'] | ||
}, function(err, results){ | ||
@@ -430,7 +423,7 @@ test.ok(!err); | ||
module.exports["undefined instance error"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooks: new Task('getGenre.getBooks'), | ||
}).execute({ | ||
genreName: 'Fictiony' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooks: ['getGenre.getBooks'] | ||
}, function(e, results){ | ||
@@ -445,11 +438,11 @@ test.ok(e, 'got an error'); | ||
module.exports["undefined instance error 2"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooks: new Task(Book.findByGenreId, 'getGenre.id.undef_value.err'), | ||
}).execute({ | ||
genreName: 'Fiction' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooks: [Book.findByGenreId, 'getGenre.id.undef_value.err'] | ||
}, function(e, results){ | ||
test.ok(e, 'got an error'); | ||
test.equals(e.name, "ArgumentNullError", "got proper error"); | ||
test.equals(e.message, 'Not Found: "id.undef_value" for getGenre with genreName "Fiction"') | ||
test.equals(e.message, 'Not Found: "id.undef_value" with genreName "Fiction"') | ||
test.done(); | ||
@@ -459,8 +452,8 @@ }); | ||
module.exports["multiple functions error"] = function(test){ | ||
module.exports["function as arg error"] = function(test){ | ||
try { | ||
flow({ | ||
new Flow({ | ||
getBook: new Task(Book.getById, Book.getById, 'bookId'), | ||
}).execute({ | ||
bookId: 1 | ||
}, { | ||
getBook: [Book.getById, Book.getById, 'bookId'] | ||
}, function(err, results){ | ||
@@ -472,4 +465,4 @@ test.fail(null, null, "no error received"); | ||
test.ok(e, 'got an error'); | ||
test.equals(e.name, "FlowTaskError", "got FlowTaskError"); | ||
test.equals(e.message, "Flow error in 'getBook': More than one function specified (at index 0 and 1).", "error message match") | ||
test.equals(e.name, "TypeError", "got TypeError"); | ||
test.equals(e.message, 'args must contain only string names of tasks or data.', "error message match") | ||
test.done(); | ||
@@ -479,33 +472,9 @@ } | ||
// module.exports["multiple instance functions error"] = function(test){ | ||
// try { | ||
// flow({ | ||
// bookId: 1 | ||
// }, { | ||
// getBook: [Book.getById, 'bookId'], | ||
// getBookAuthor: ['getBook.getAuthor', 'getBook.getAuthor'] | ||
// }, function(err, results){ | ||
// test.fail(null, null, "no error received"); | ||
// test.done(); | ||
// }); | ||
// } catch(e) { | ||
// test.ok(e, 'got an error'); | ||
// test.equals(e.name, "FlowTaskError", "got FlowTaskError"); | ||
// test.equals(e.message, "Flow error in 'getBookAuthor': More than one function specified (at index 1 and 2).", "error message match") | ||
// test.done(); | ||
// } | ||
// } | ||
module.exports["unknown symbol error"] = function(test){ | ||
try { | ||
flow({ | ||
bookId: 1 | ||
}, { | ||
getBook: [Book.getById, 'bookId'], | ||
getAuthor: ['getBook.notafunction'] | ||
}, function(err, results){ | ||
test.fail(null, null, "no error received"); | ||
test.done(); | ||
}); | ||
} catch(e) { | ||
new Flow({ | ||
getBook: new Task(Book.getById, 'bookId'), | ||
getAuthor: new Task('getBook.notafunction'), | ||
}).execute({ | ||
bookId: 1 | ||
}, function(e, results){ | ||
test.ok(e, 'got an error'); | ||
@@ -515,3 +484,3 @@ test.equals(e.name, "FlowTaskError", "got FlowTaskError"); | ||
test.done(); | ||
} | ||
}); | ||
} | ||
@@ -521,7 +490,7 @@ | ||
try { | ||
flow({ | ||
new Flow({ | ||
getBook: new Task(Book.getById, 'bookId'), | ||
getAuthor: new Task('notafunction'), | ||
}).execute({ | ||
bookId: 1 | ||
}, { | ||
getBook: [Book.getById, 'bookId'], | ||
getAuthor: ['notafunction'] | ||
}, function(err, results){ | ||
@@ -541,7 +510,7 @@ test.fail(null, null, "no error received"); | ||
try { | ||
flow({ | ||
new Flow({ | ||
getBook: new Task(Book.getById, 'bookId'), | ||
getAuthor: new Task(Book.getAuthorByBookId, 'getBook'), | ||
}).execute({ | ||
bookId: 1 | ||
}, { | ||
getBook: [Book.getById, 'bookId'], | ||
getAuthor: [Book.getAuthorByBookId, 'getBook'] | ||
}, function(err, results){ | ||
@@ -553,4 +522,4 @@ test.fail(null, null, "no error received"); | ||
test.ok(e, 'got an error'); | ||
test.equals(e.name, "FlowTaskError", "got FlowTaskError"); | ||
test.equals(e.message, "Flow error in 'getAuthor': Unknown symbol at index '0' must be either the name of a task, the name of data, or be the name of a function on the result of a task or data", "error message match") | ||
test.equals(e.name, "ArgumentNullError", "got ArgumentNullError"); | ||
test.equals(e.message, 'Missing argument: fn', "error message match") | ||
test.done(); | ||
@@ -562,7 +531,7 @@ } | ||
try { | ||
flow({ | ||
new Flow({ | ||
getBook: new Task(Book.getById, 'bookId'), | ||
getAuthor: new Task(), | ||
}).execute({ | ||
bookId: 1 | ||
}, { | ||
getBook: [Book.getById, 'bookId'], | ||
getAuthor: [] | ||
}, function(err, results){ | ||
@@ -574,4 +543,4 @@ test.fail(null, null, "no error received"); | ||
test.ok(e, 'got an error'); | ||
test.equals(e.name, "FlowTaskError", "got FlowTaskError"); | ||
test.equals(e.message, "Flow error in 'getAuthor': Function required.", "error message match") | ||
test.equals(e.name, "ArgumentNullError", "got ArgumentNullError"); | ||
test.equals(e.message, "Missing argument: fn", "error message match") | ||
test.done(); | ||
@@ -581,20 +550,14 @@ } | ||
module.exports["missing function in task args error"] = function(test){ | ||
try { | ||
flow({ | ||
new Flow({ | ||
getBook: new Task(Book.getById, 'bookId'), | ||
getAuthor: new Task('getBook'), | ||
}).execute({ | ||
bookId: 1 | ||
}, { | ||
getBook: [Book.getById, 'bookId'], | ||
getAuthor: ['getBook'] | ||
}, function(err, results){ | ||
test.fail(null, null, "no error received"); | ||
}, function(e, results){ | ||
test.ok(e, 'got an error'); | ||
test.equals(e.name, "FlowTaskError", "got FlowTaskError"); | ||
test.equals(e.message, "Flow error in 'getAuthor': Not a function: getBook", "error message match") | ||
test.done(); | ||
}); | ||
} catch(e) { | ||
test.ok(e, 'got an error'); | ||
test.equals(e.name, "FlowTaskError", "got FlowTaskError"); | ||
test.equals(e.message, "Flow error in 'getAuthor': Function required.", "error message match") | ||
test.done(); | ||
} | ||
} | ||
@@ -604,7 +567,7 @@ | ||
try { | ||
flow({ | ||
new Flow({ | ||
getBook: new Task(Book.getById, 'bookId'), | ||
getAuthor: new Task(3), | ||
}).execute({ | ||
bookId: 1 | ||
}, { | ||
getBook: [Book.getById, 'bookId'], | ||
getAuthor: 'getBook' | ||
}, function(err, results){ | ||
@@ -616,4 +579,4 @@ test.fail(null, null, "no error received"); | ||
test.ok(e, 'got an error'); | ||
test.equals(e.name, "FlowTaskError", "got FlowTaskError"); | ||
test.equals(e.message, "Flow error in 'getAuthor': Invalid flow type. Must be function, subFlow, or array.", "error message match") | ||
test.equals(e.name, "TypeError", "got TypeError"); | ||
test.equals(e.message, "Task fn must be a string or a function", "error message match") | ||
test.done(); | ||
@@ -625,7 +588,7 @@ } | ||
try { | ||
flow({ | ||
new Flow({ | ||
getBook: new Task(Book.getById, 'bookId'), | ||
getAuthor: new Task(Author.getById), | ||
}).execute({ | ||
bookId: 1 | ||
}, { | ||
getBook: [Book.getById, 'bookId'], | ||
getAuthor: [Author.getById] | ||
}, function(err, results){ | ||
@@ -644,13 +607,11 @@ test.fail(null, null, "no error received"); | ||
module.exports["subFlow execution"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooksByGenre: new Task('getGenre.getBooks'), | ||
getAuthors: new Flow('getBooksByGenre', { | ||
getBookAuthor: new Task('getBooksByGenre.getAuthor') | ||
}) | ||
}).execute({ | ||
genreName: 'Fantasy' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooksByGenre: ['getGenre.getBooks'], | ||
getAuthors: flow.subFlow('getBooksByGenre', { | ||
getBookAuthor: ['getBooksByGenre.getAuthor'] | ||
}) | ||
}, function(err, results){ | ||
@@ -678,12 +639,11 @@ test.ok(!err, 'no error'); | ||
module.exports["subFlow execution with context"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooksByGenre: new Task('getGenre.getBooks'), | ||
getAuthors: new Flow('getBooksByGenre', { | ||
getBookAuthor: new Task(Author.getById, 'authorId') | ||
}) | ||
}).execute({ | ||
genreName: 'Fantasy' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooksByGenre: ['getGenre.getBooks'], | ||
getAuthors: flow.subFlow('getBooksByGenre', { | ||
getBookAuthor: [Author.getById, 'authorId'] | ||
}) | ||
}, function(err, results){ | ||
@@ -711,12 +671,11 @@ test.ok(!err, 'no error'); | ||
module.exports["subFlow execution using subFlow"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooksByGenre: new Task('getGenre.getBooks'), | ||
getAuthors: new Flow('getBooksByGenre', { | ||
getBookAuthor: new Task('getBooksByGenre.getAuthor') | ||
}) | ||
}).execute({ | ||
genreName: 'Fantasy' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooksByGenre: ['getGenre.getBooks'], | ||
getAuthors: flow.subFlow('getBooksByGenre', { | ||
getBookAuthor: ['getBooksByGenre.getAuthor'] | ||
}) | ||
}, function(err, results){ | ||
@@ -744,15 +703,13 @@ test.ok(!err, 'no error'); | ||
module.exports["subFlow execution with prereqs"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooksByGenre: new Task('getGenre.getBooks'), | ||
getAuthors: new Flow('getBooksByGenre', { | ||
getHambly2: new Task(Author.getById, 'getHambly') | ||
}), | ||
getHambly: new Task(Author.getByName, 'authorName') | ||
}).execute({ | ||
genreName: 'Fantasy', | ||
authorName: 'Barbara Hambly' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooksByGenre: ['getGenre.getBooks'], | ||
getAuthors: flow.subFlow('getBooksByGenre', { | ||
getHambly2: [Author.getById, 'getHambly'] | ||
}), | ||
getHambly: [Author.getByName, 'authorName'] | ||
}, function(err, results){ | ||
@@ -782,18 +739,55 @@ test.ok(!err, 'no error'); | ||
/* | ||
getAuthors: *getBooksByGenre, t.getBookAuthor, t.getBooksByAuthor, getHambly | ||
*/ | ||
module.exports["two nested subflows with prereqs"] = function(test){ | ||
flow({ | ||
// new Flow({ | ||
// genreName: 'Fantasy', | ||
// authorName: 'Barbara Hambly' | ||
// }); | ||
// flow.tasks.getGenre = new Task(Genre.getByName, 'genreName'); | ||
// flow.tasks.getBooksByGenre = new Task('getGenre.getBooks'); | ||
// var subflow = flow.tasks.getAuthors = new Flow('getBooksByGenre'); | ||
// subflow.tasks.getBookAuthor = new Task('getBooksByGenre.getAuthor'); | ||
// subflow.tasks.getBooksByAuthor = new Task(Book.findByAuthorId, 'getBookAuthor'); | ||
// var subflow2 = subflow.tasks.getManyHamblies = new Flow('getBooksByAuthor'); | ||
// subflow2.tasks.getHambly2 = new Task(Author.getById, 'getHambly'); | ||
// flow.tasks.getHambly = new Task(Author.getByName, 'authorName'); | ||
// new Flow(); | ||
// flow.tasks = { | ||
// getGenre: new Task(Genre.getByName, flow.data.genreName), | ||
// getBooksByGenre: new Task(flow.tasks.getGenre.getBooks), | ||
// getAuthors: new Flow(flow.tasks.getBooksByGenre, { | ||
// getBookAuthor: flow.data.getAuthor, | ||
// getBooksByAuthor: new Task(Book.findByAuthorId, flow.tasks.getBookAuthor), | ||
// getManyHamblies: new Flow(flow.tasks.getBooksByAuthor, { | ||
// getHambly2: new Task(Author.getById, flow.tasks.getHambly) | ||
// }) | ||
// }), | ||
// getHambly: new Task(Author.getByName, flow.data.authorName) | ||
// }); | ||
// flow.execute({ | ||
// genreName: 'Fantasy', | ||
// authorName: 'Barbara Hambly' | ||
// }, function(err, results){ | ||
// }); | ||
new Flow({ | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooksByGenre: new Task('getGenre.getBooks'), | ||
getAuthors: new Flow('getBooksByGenre', { | ||
getBookAuthor: new Task('getAuthor'), | ||
getBooksByAuthor: new Task(Book.findByAuthorId, 'getBookAuthor'), | ||
getManyHamblies: new Flow('getBooksByAuthor', { | ||
getHambly2: new Task(Author.getById, 'getHambly') | ||
}) | ||
}), | ||
getHambly: new Task(Author.getByName, 'authorName') | ||
}).execute({ | ||
genreName: 'Fantasy', | ||
authorName: 'Barbara Hambly' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooksByGenre: ['getGenre.getBooks'], | ||
getAuthors: flow.subFlow('getBooksByGenre', { | ||
getBookAuthor: ['getBooksByGenre.getAuthor'], | ||
getBooksByAuthor: [Book.findByAuthorId, 'getBookAuthor'], | ||
getManyHamblies: flow.subFlow('getBooksByAuthor', { | ||
getHambly2: [Author.getById, 'getHambly'] | ||
}) | ||
}), | ||
getHambly: [Author.getByName, 'authorName'] | ||
}, function(err, results){ | ||
@@ -860,12 +854,12 @@ test.ok(!err, 'no error'); | ||
module.exports["subflow with prereqs and result instance parameter"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooksByGenre: new Task('getGenre.getBooks'), | ||
getAuthors: new Flow('getBooksByGenre', { | ||
getHambly2: new Task(Author.getById, 'getHambly.id') | ||
}), | ||
getHambly: new Task(Author.getByName, 'authorName'), | ||
}).execute({ | ||
genreName: 'Fantasy', | ||
authorName: 'Barbara Hambly' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooksByGenre: ['getGenre.getBooks'], | ||
getAuthors: flow.subFlow('getBooksByGenre', { | ||
getHambly2: [Author.getById, 'getHambly.id'] | ||
}), | ||
getHambly: [Author.getByName, 'authorName'] | ||
}, function(err, results){ | ||
@@ -897,10 +891,10 @@ test.ok(!err, 'no error'); | ||
try { | ||
flow({ | ||
new Flow({ | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooksByGenre: new Task('getGenre.getBooks'), | ||
getAuthors: new Flow('', { | ||
getBookAuthor: new Task('getBooksByGenre.getAuthor') | ||
}) | ||
}).execute({ | ||
genreName: 'Fantasy' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooksByGenre: ['getGenre.getBooks'], | ||
getAuthors: flow.subFlow('', { | ||
getBookAuthor: ['getBooksByGenre.getAuthor'] | ||
}) | ||
}, function(err, results){ | ||
@@ -912,4 +906,4 @@ test.fail(null, null, "no error received"); | ||
test.ok(e, 'got an error'); | ||
test.equals(e.name, "Error", "got Error"); | ||
test.equals(e.message, "SubFlow error: No data given for subFlow. Provide the name of a task or data from the parent flow.", "error message match") | ||
test.equals(e.name, "ArgumentNullError", "got ArgumentNullError"); | ||
test.equals(e.message, "Missing argument: context_name", "error message match") | ||
test.done(); | ||
@@ -921,9 +915,10 @@ } | ||
try { | ||
flow({ | ||
new Flow({ | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooksByGenre: new Task('getGenre.getBooks'), | ||
getAuthors: new Flow('getBooksByGenre') | ||
}).execute({ | ||
genreName: 'Fantasy' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooksByGenre: ['getGenre.getBooks'], | ||
getAuthors: flow.subFlow('getBooksByGenre', null) | ||
}, function(err, results){ | ||
console.log(results); | ||
test.fail(null, null, "no error received"); | ||
@@ -935,3 +930,3 @@ test.done(); | ||
test.equals(e.name, "Error", "got Error"); | ||
test.equals(e.message, "SubFlow error: No tasks given for subFlow.", "error message match") | ||
test.equals(e.message, "No tasks given for Flow.", "error message match") | ||
test.done(); | ||
@@ -941,13 +936,12 @@ } | ||
module.exports["subflow with bad data name"] = function(test){ | ||
try { | ||
flow({ | ||
new Flow({ | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooksByGenre: new Task('getGenre.getBooks'), | ||
getAuthors: new Flow('asdf', { | ||
getBookAuthor: new Task('getBooksByGenre.getAuthor') | ||
}) | ||
}).execute({ | ||
genreName: 'Fantasy' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooksByGenre: ['getGenre.getBooks'], | ||
getAuthors: flow.subFlow('asdf', { | ||
getBookAuthor: ['getBooksByGenre.getAuthor'] | ||
}) | ||
}, function(err, results){ | ||
@@ -960,3 +954,3 @@ test.fail(null, null, "no error received"); | ||
test.equals(e.name, "FlowTaskError", "got Error"); | ||
test.equals(e.message, "Flow error in 'getAuthors': SubFlow data 'asdf' does not exist. Provide the name of a task or data from the parent flow. Possible values include: getGenre, getBooksByGenre, genreName", "error message match") | ||
test.equals(e.message, "Flow error in 'getAuthors': Subflow data 'asdf' does not exist. Provide the name of a flow, task or data from the parent flow. Possible values include: genreName, getGenre, getBooksByGenre", "error message match") | ||
test.done(); | ||
@@ -966,12 +960,13 @@ } | ||
module.exports["subflow task with own data name"] = function(test){ | ||
try { | ||
flow({ | ||
new Flow({ | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooksByGenre: new Task('getGenre.getBooks'), | ||
getAuthors: new Flow('getAuthors', { | ||
getBookAuthor: new Task('getBooksByGenre.getAuthor') | ||
}) | ||
}).execute({ | ||
genreName: 'Fantasy' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooksByGenre: ['getGenre.getBooks'], | ||
getAuthors: flow.subFlow('getAuthors', { | ||
getBookAuthor: ['getBooksByGenre.getAuthor'] | ||
}) | ||
}, function(err, results){ | ||
@@ -984,3 +979,3 @@ test.fail(null, null, "no error received"); | ||
test.equals(e.name, "FlowTaskError", "got Error"); | ||
test.equals(e.message, "Flow error in 'getAuthors': SubFlow data 'getAuthors' does not exist. Provide the name of a task or data from the parent flow. Possible values include: getGenre, getBooksByGenre, genreName", "error message match") | ||
test.equals(e.message, "Flow error in 'getAuthors': Subflow data 'getAuthors' does not exist. Provide the name of a flow, task or data from the parent flow. Possible values include: genreName, getGenre, getBooksByGenre", "error message match") | ||
test.done(); | ||
@@ -990,15 +985,16 @@ } | ||
module.exports["subflow with explicit prereq"] = function(test){ | ||
flow({ | ||
var flow = new Flow({ | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
assertGenreExistence: new Task(Genre.assertExistence, 'getGenre'), | ||
getBooksByGenre: new Task('getGenre.getBooks'), | ||
getAuthors: new Flow('getBooksByGenre', { | ||
getBookAuthor: new Task(Author.getById, 'authorId') | ||
}).requires('assertGenreExistence') | ||
}); | ||
flow.execute({ | ||
genreName: 'Fantasy' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
assertGenreExistence: [Genre.assertExistence, 'getGenre'], | ||
getBooksByGenre: ['getGenre.getBooks'], | ||
getAuthors: ['assertGenreExistence', flow.subFlow('getBooksByGenre', { | ||
getBookAuthor: [Author.getById, 'authorId'] | ||
})] | ||
}, function(err, results){ | ||
test.ok(!err, 'no error'); | ||
test.deepEqual(flow.flows.getAuthors._plan.auto_task.slice(0, flow.flows.getAuthors._plan.auto_task.length - 1), ['getBooksByGenre', 'assertGenreExistence']) | ||
test.deepEqual(results, { | ||
@@ -1025,37 +1021,13 @@ genreName: 'Fantasy', | ||
module.exports["subflow out of order"] = function(test){ | ||
try { | ||
flow({ | ||
genreName: 'Fantasy' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
assertGenreExistence: [Genre.assertExistence, 'getGenre'], | ||
getBooksByGenre: ['getGenre.getBooks'], | ||
getAuthors: [flow.subFlow('getBooksByGenre', { | ||
getBookAuthor: [Author.getById, 'authorId'] | ||
}), 'assertGenreExistence'] | ||
}, function(err, results){ | ||
test.fail(null, null, "no error received"); | ||
test.done(); | ||
}); | ||
} catch(e){ | ||
test.ok(e, 'got an error'); | ||
test.equals(e.name, "FlowTaskError", "got Error"); | ||
test.equals(e.message, "Flow error in 'getAuthors': SubFlows must be at the last index.", "error message match") | ||
test.done(); | ||
} | ||
} | ||
module.exports["subflow in task array with bad data name"] = function(test){ | ||
try { | ||
flow({ | ||
new Flow({ | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
assertGenreExistence: new Task(Genre.assertExistence, 'getGenre'), | ||
getBooksByGenre: new Task('getGenre.getBooks'), | ||
getAuthors: new Flow('asdf', { | ||
getBookAuthor: new Task(Author.getById, 'authorId') | ||
}).requires('assertGenreExistence') | ||
}).execute({ | ||
genreName: 'Fantasy' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
assertGenreExistence: [Genre.assertExistence, 'getGenre'], | ||
getBooksByGenre: ['getGenre.getBooks'], | ||
getAuthors: ['assertGenreExistence', flow.subFlow('asdf', { | ||
getBookAuthor: [Author.getById, 'authorId'] | ||
})] | ||
}, function(err, results){ | ||
@@ -1068,3 +1040,3 @@ test.fail(null, null, "no error received"); | ||
test.equals(e.name, "FlowTaskError", "got Error"); | ||
test.equals(e.message, "Flow error in 'getAuthors': SubFlow data 'asdf' does not exist. Provide the name of a task or data from the parent flow. Possible values include: getGenre, assertGenreExistence, getBooksByGenre, genreName", "error message match") | ||
test.equals(e.message, "Flow error in 'getAuthors': Subflow data 'asdf' does not exist. Provide the name of a flow, task or data from the parent flow. Possible values include: genreName, getGenre, assertGenreExistence, getBooksByGenre", "error message match") | ||
test.done(); | ||
@@ -1074,14 +1046,13 @@ } | ||
module.exports["subflow in task array with own data name"] = function(test){ | ||
try { | ||
flow({ | ||
new Flow({ | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
assertGenreExistence: new Task(Genre.assertExistence, 'getGenre'), | ||
getBooksByGenre: new Task('getGenre.getBooks'), | ||
getAuthors: new Flow('getAuthors', { | ||
getBookAuthor: new Task(Author.getById, 'authorId') | ||
}).requires('assertGenreExistence') | ||
}).execute({ | ||
genreName: 'Fantasy' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
assertGenreExistence: [Genre.assertExistence, 'getGenre'], | ||
getBooksByGenre: ['getGenre.getBooks'], | ||
getAuthors: ['assertGenreExistence', flow.subFlow('getAuthors', { | ||
getBookAuthor: [Author.getById, 'authorId'] | ||
})] | ||
}, function(err, results){ | ||
@@ -1094,3 +1065,3 @@ test.fail(null, null, "no error received"); | ||
test.equals(e.name, "FlowTaskError", "got Error"); | ||
test.equals(e.message, "Flow error in 'getAuthors': SubFlow data 'getAuthors' does not exist. Provide the name of a task or data from the parent flow. Possible values include: getGenre, assertGenreExistence, getBooksByGenre, genreName", "error message match") | ||
test.equals(e.message, "Flow error in 'getAuthors': Subflow data 'getAuthors' does not exist. Provide the name of a flow, task or data from the parent flow. Possible values include: genreName, getGenre, assertGenreExistence, getBooksByGenre", "error message match") | ||
test.done(); | ||
@@ -1100,12 +1071,10 @@ } | ||
module.exports["single data subflow execution"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooksByGenre: new Flow('getGenre', { | ||
getBooksByGenre: new Task(Book.findByGenreId, 'id') | ||
}) | ||
}).execute({ | ||
genreName: 'Fantasy' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooksByGenre: flow.subFlow('getGenre', { | ||
getBooksByGenre: [Book.findByGenreId, 'id'] | ||
}) | ||
}, function(err, results){ | ||
@@ -1124,17 +1093,11 @@ test.ok(!err, 'no error'); | ||
module.exports["single null data subflow execution"] = function(test){ | ||
try { | ||
flow({ | ||
genreName: 'Chainsaw slashers' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooksByGenre: flow.subFlow('getGenre', { | ||
getBooksByGenre2: [Book.findByGenreId, 'id'] | ||
}) | ||
}, function(err, results){ | ||
test.fail(null, null, "no error received"); | ||
test.done(); | ||
}); | ||
} catch(e){ | ||
new Flow({ | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooksByGenre: new Flow('getGenre', { | ||
getBooksByGenre2: new Task(Book.findByGenreId, 'id') | ||
}) | ||
}).execute({ | ||
genreName: 'Chainsaw slashers' | ||
}, function(e, results){ | ||
test.ok(e, 'got an error'); | ||
@@ -1144,14 +1107,14 @@ test.equals(e.name, "FlowTaskError", "got Error"); | ||
test.done(); | ||
} | ||
}); | ||
} | ||
module.exports["subflow with result instance parameter"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getGenre: new Task(Genre.getByName, 'genreName'), | ||
getBooksByGenre: new Task('getGenre.getBooks'), | ||
getAuthors: new Flow('getBooksByGenre', { | ||
getBookAuthor: new Task(Author.getById, 'getBooksByGenre.authorId') | ||
}) | ||
}).execute({ | ||
genreName: 'Fantasy' | ||
}, { | ||
getGenre: [Genre.getByName, 'genreName'], | ||
getBooksByGenre: ['getGenre.getBooks'], | ||
getAuthors: flow.subFlow('getBooksByGenre', { | ||
getBookAuthor: [Author.getById, 'getBooksByGenre.authorId'] | ||
}) | ||
}, function(err, results){ | ||
@@ -1179,42 +1142,10 @@ test.ok(!err, 'no error'); | ||
module.exports["asynchronous task execution"] = function(test){ | ||
flow({ | ||
id: 3, | ||
}, { | ||
getBook: flow.asyncTask(Book.getById, 'id'), | ||
getAuthor: flow.asyncTask(Author.getById, 'getBook.authorId'), | ||
}, function(err, results){ | ||
test.deepEqual({ | ||
id: 3, | ||
getBook: Book.all[3], | ||
getAuthor: Author.all[1], | ||
}, results); | ||
test.done(); | ||
}); | ||
} | ||
module.exports["asynchronous task execution with array"] = function(test){ | ||
flow({ | ||
id: 3, | ||
}, { | ||
getBook: flow.asyncTask([Book.getById, 'id']), | ||
getAuthor: flow.asyncTask([Author.getById, 'getBook.authorId']), | ||
}, function(err, results){ | ||
test.deepEqual({ | ||
id: 3, | ||
getBook: Book.all[3], | ||
getAuthor: Author.all[1], | ||
}, results); | ||
test.done(); | ||
}); | ||
} | ||
module.exports["synchronous task execution"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
getBook: new Task(Book.getById, 'id'), | ||
getAuthor: new Task(Author.getById, 'getBook.authorId'), | ||
getBookAuthorData: new Fn(us.extend, 'new_object', 'getBook', 'getAuthor') | ||
}).execute({ | ||
id: 3, | ||
new_object: {} | ||
}, { | ||
getBook: [Book.getById, 'id'], | ||
getAuthor: [Author.getById, 'getBook.authorId'], | ||
getBookAuthorData: flow.syncTask(us.extend, 'new_object', 'getBook', 'getAuthor') | ||
}, function(err, results){ | ||
@@ -1232,63 +1163,2 @@ test.deepEqual({ | ||
module.exports["synchronous task execution with array"] = function(test){ | ||
flow({ | ||
id: 3, | ||
new_object: {} | ||
}, { | ||
getBook: [Book.getById, 'id'], | ||
getAuthor: [Author.getById, 'getBook.authorId'], | ||
getBookAuthorData: flow.syncTask([us.extend, 'new_object', 'getBook', 'getAuthor']) | ||
}, function(err, results){ | ||
test.deepEqual({ | ||
id: 3, | ||
new_object: us.extend({}, Book.all[3], Author.all[1]), | ||
getBook: Book.all[3], | ||
getAuthor: Author.all[1], | ||
getBookAuthorData: us.extend({}, Book.all[3], Author.all[1]) | ||
}, results); | ||
test.done(); | ||
}); | ||
} | ||
// module.exports["array result data execution"] = function(test){ | ||
// var authors, fantasyAuthors; | ||
// fantasyAuthors = new FnFlow({ | ||
// genreName: 'Fantasy', | ||
// }) | ||
// .addTask('getGenre', Genre.getByName, fantasyAuthors.data('genreName')); | ||
// .addTask('getBooksByGenre', fantasyAuthors.task.getGenre, fantasyAuthors.task.getGenre.call('getBooks')); | ||
// .addTask('getAuthors', (authors = new FnFlow(fantasyAuthors.task.getBooksByGenre)) | ||
// .addTask('getBookAuthor', authors.data.call('getAuthor')) | ||
// .addTask('getBookAuthor', Author.getById, author.data('authorId')) | ||
// ) | ||
// }, function(err, results){ | ||
// test.ok(!err, 'no error'); | ||
// test.deepEqual(results, { | ||
// genreName: 'Fantasy', | ||
// getGenre: Genre.all[1], | ||
// getBooksByGenre: [Book.all[7], Book.all[8], Book.all[9], Book.all[10], Book.all[11]], | ||
// getAuthors: [{ | ||
// getBookAuthor: Author.all[6] | ||
// }, { | ||
// getBookAuthor: Author.all[6] | ||
// }, { | ||
// getBookAuthor: Author.all[5] | ||
// }, { | ||
// getBookAuthor: Author.all[5] | ||
// }, { | ||
// getBookAuthor: Author.all[5] | ||
// }] | ||
// }); | ||
// test.done(); | ||
// }); | ||
// } | ||
// flow({ | ||
// some_data: 2, | ||
// some_value: '3.2', | ||
// }, { | ||
// test1: flow.asyncTask([Class.doSomething, 'some_data']), | ||
// test3: flow.syncTask([parseInt, 'some_value']) | ||
// }) |
@@ -1,2 +0,2 @@ | ||
var flow = require('../lib/fnFlow').flow; | ||
var Flow = require('../lib/fnFlow').Flow, Task = Flow.Task; | ||
var us = require('underscore'); | ||
@@ -19,6 +19,6 @@ var errors = require('common-errors'); | ||
module.exports["flow task assert exists"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
book: new Task(Book.getById, 'bookId').assertExists() | ||
}).execute({ | ||
bookId: 1 | ||
}, { | ||
book: flow.asyncTask(Book.getById, 'bookId').assertExists() | ||
}, function(err, results){ | ||
@@ -31,6 +31,6 @@ test.ok(!err, 'no error'); | ||
module.exports["flow task assert exists fail"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
book: new Task(Book.getById, 'bookId').assertExists(), | ||
}).execute({ | ||
bookId: 1000 | ||
}, { | ||
book: flow.asyncTask(Book.getById, 'bookId').assertExists() | ||
}, function(err, results){ | ||
@@ -46,7 +46,7 @@ test.ok(err, 'got error'); | ||
module.exports["flow task assert exists fail 2"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
book: new Task(Book.getById, 'bookId'), | ||
bookSeries: new Task('book.getBookSeries').assertExists(), | ||
}).execute({ | ||
bookId: 4 | ||
}, { | ||
book: flow.asyncTask(Book.getById, 'bookId'), | ||
bookSeries: flow.asyncTask('book.getBookSeries').assertExists() | ||
}, function(err, results){ | ||
@@ -56,3 +56,3 @@ test.ok(err, 'got error'); | ||
test.equals(err && err.argumentName, 'bookSeries'); | ||
test.equals(err && err.message, 'Not Found: "bookSeries" for book with bookId 4'); | ||
test.equals(err && err.message, 'Not Found: "bookSeries" with bookId 4'); | ||
test.done(); | ||
@@ -63,10 +63,10 @@ }); | ||
module.exports["flow task assert exists fail 3"] = function(test){ | ||
flow({ | ||
new Flow({ | ||
genre: new Task(Genre.getByName, 'genreName'), | ||
author: new Task(Author.getByName, 'authorName'), | ||
book: new Task(Book.getFirstByGenreAndAuthor, 'genre', 'author'), | ||
bookSeries: new Task('book.getBookSeries').assertExists(), | ||
}).execute({ | ||
genreName: 'Sports', | ||
authorName: 'Tom Coughlin' | ||
}, { | ||
genre: [Genre.getByName, 'genreName'], | ||
author: [Author.getByName, 'authorName'], | ||
book: flow.asyncTask(Book.getFirstByGenreAndAuthor, 'genre', 'author'), | ||
bookSeries: flow.asyncTask('book.getBookSeries').assertExists() | ||
}, function(err, results){ | ||
@@ -76,3 +76,3 @@ test.ok(err, 'got error'); | ||
test.equals(err && err.argumentName, 'bookSeries'); | ||
test.equals(err && err.message, 'Not Found: "bookSeries" for book, genre and author with genreName "Sports" and authorName "Tom Coughlin"'); | ||
test.equals(err && err.message, 'Not Found: "bookSeries" with genreName "Sports" and authorName "Tom Coughlin"'); | ||
test.done(); | ||
@@ -87,10 +87,10 @@ }); | ||
flow({ | ||
new Flow({ | ||
genre: new Task(Genre.getByName, 'genreName'), | ||
author: new Task(Author.getByName, 'authorName'), | ||
book: new Task(Book.getFirstByGenreAndAuthor, 'genre', 'author'), | ||
bookSeries: new Task(testFunction, 'book'), | ||
}).execute({ | ||
genreName: 'Sports', | ||
authorName: 'Tom Coughlin' | ||
}, { | ||
genre: [Genre.getByName, 'genreName'], | ||
author: [Author.getByName, 'authorName'], | ||
book: flow.asyncTask(Book.getFirstByGenreAndAuthor, 'genre', 'author'), | ||
bookSeries: flow.asyncTask(testFunction, 'book') | ||
}, function(err, results){ | ||
@@ -100,3 +100,3 @@ test.ok(err, 'got error'); | ||
test.equals(err && err.argumentName, 'book'); | ||
test.equals(err && err.message, 'Not Found: "book" for genre and author with genreName "Sports" and authorName "Tom Coughlin"'); | ||
test.equals(err && err.message, 'Not Found: "book" with genreName "Sports" and authorName "Tom Coughlin"'); | ||
test.done(); | ||
@@ -106,3 +106,2 @@ }); | ||
module.exports["subFlow execution with assert exists failure"] = function(test){ | ||
@@ -113,11 +112,11 @@ var testFunction = function(book, cb) { | ||
flow({ | ||
new Flow({ | ||
genre: new Task(Genre.getByName, 'genreName'), | ||
books: new Task('genre.getBooks'), | ||
authors: new Flow('books', { | ||
test_null: new Task(testFunction, 'books'), | ||
author: new Task(Author.getById, 'test_null').assertExists() | ||
}) | ||
}).execute({ | ||
genreName: 'Fantasy' | ||
}, { | ||
genre: [Genre.getByName, 'genreName'], | ||
books: ['genre.getBooks'], | ||
authors: [flow.subFlow('books', { | ||
test_null: [testFunction, 'books'], | ||
author: flow.asyncTask(Author.getById, 'test_null').assertExists() | ||
})] | ||
}, function(err, results){ | ||
@@ -127,51 +126,5 @@ test.ok(err, 'got error'); | ||
test.equals(err && err.argumentName, 'author'); | ||
test.equals(err && err.message, 'Not Found: "author" for test_null, books and genre with genreName "Fantasy"'); | ||
test.equals(err && err.message, 'Not Found: "author" with genreName "Fantasy"'); | ||
test.done(); | ||
}); | ||
} | ||
module.exports["subFlow execution with argument null error"] = function(test){ | ||
var testFunction = function(author, cb) { | ||
return cb(new errors.ArgumentNull('author')); | ||
} | ||
flow({ | ||
test_data: "asdflkj", | ||
genreName: 'Fantasy' | ||
}, { | ||
genre: [Genre.getByName, 'genreName'], | ||
books: ['genre.getBooks'], | ||
authors: [flow.subFlow('books', { | ||
author: flow.asyncTask(Author.getById, 'books.no_data'), | ||
test_null: [testFunction, 'author'] | ||
})] | ||
}, function(err, results){ | ||
test.ok(err, 'got error'); | ||
test.equals(err && err.name, 'ArgumentNullError'); | ||
test.equals(err && err.argumentName, 'author'); | ||
test.equals(err && err.message, 'Not Found: "author" for books and genre with genreName "Fantasy"'); | ||
test.done(); | ||
}); | ||
} | ||
module.exports["subFlow execution with assert exists failure on context"] = function(test){ | ||
flow({ | ||
data: [{ | ||
genre_name: 'Fantasy' | ||
}, { | ||
genre_name: 'Sports' | ||
}, { | ||
genre_name: 'Non-Fantasy' | ||
}] | ||
}, { | ||
do_all: flow.subFlow('data', { | ||
genre: flow.asyncTask(Genre.getByName, 'genre_name').assertExists(), | ||
}) | ||
}, function(err, results){ | ||
test.ok(err, 'got error'); | ||
test.equals(err && err.name, 'ArgumentNullError'); | ||
test.equals(err && err.argumentName, 'genre'); | ||
test.equals(err && err.message, 'Not Found: "genre" with genre_name "Non-Fantasy"'); | ||
test.done(); | ||
}); | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
109121
19
187
1762