Created
February 10, 2020 23:25
-
-
Save danielnieto/bb74d50c43023c39574303c9d9bb7989 to your computer and use it in GitHub Desktop.
a hacked flourine version to report execution times
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require('colors') | |
Class('Flow').includes(CustomEventSupport, NodeSupport)({ | |
prototype: { | |
/** | |
holder for the name of the flow | |
@property name <public> [String] | |
**/ | |
name: '', | |
/** | |
initializer for the class | |
@property init <public> [Function] | |
@argument config <optional> [Object] | |
**/ | |
init: function init(config) { | |
Object.keys(config).forEach(function (property) { | |
this[property] = config[property]; | |
}, this); | |
}, | |
/** | |
Define a step in the flow. this is a dsl like starter because returs functions. | |
flowInstance.step('name')(function (step) {...}); | |
@property step <public> [Function] | |
@argument name <required> [String] | |
@return Function a closure to the created step that you can use either to finish the step | |
creation or define more metadata. | |
**/ | |
step: function step(name) { | |
var flow, dependencies, nodeFactory; | |
flow = this; | |
dependencies = []; | |
nodeFactory = function nodeFactory(code, errorCode) { | |
var node = new FlowNode({ | |
name: name, | |
code: code, | |
errorCode: errorCode, | |
dependencies: dependencies | |
}); | |
flow.appendChild(node); | |
dependencies.forEach(function dependenciesIterator(dependencyName) { | |
flow.bind(dependencyName, function () { | |
flow.runNode(node); | |
}); | |
}); | |
flow.runNode(node); | |
}; | |
nodeFactory.dependsOn = function dependsOn() { | |
dependencies = Array.prototype.slice.call(arguments, 0); | |
return nodeFactory; | |
}; | |
return nodeFactory; | |
}, | |
/** | |
Checks for a node candidate to run, in order to run this node | |
all its preconditions must be met (dependencies completed). after this steps | |
pass it creates the appropiete data that will pass to the node and finally | |
it executes the node passing a capability object to this step that contains | |
the data from its dependencies. | |
@property runNode <public> [Function] | |
@argument node <required> [FlowNode] the node that you want to execute. | |
**/ | |
runNode: function runNode(node) { | |
var dependencies = [].concat(node.dependencies); | |
var hasRejectedDependency = false; | |
var fulfilledDependencies = dependencies.filter(function (dependencyName) { | |
if (this.hasOwnProperty(dependencyName) === false) { | |
return; | |
} | |
if (this[dependencyName].isFulfilled === true || this[dependencyName].isRejected === true) { | |
if (this[dependencyName].isRejected === true) { | |
hasRejectedDependency = true; | |
} | |
return true; | |
} | |
}, this); | |
if (dependencies.length === fulfilledDependencies.length) { | |
var nodeLikeObject = { | |
success: function nodeSuccess(data) { | |
node.fulfill(data); | |
}, | |
fail: function nodeFail(error) { | |
node.reject(error); | |
} | |
}; | |
if (hasRejectedDependency === false) { | |
nodeLikeObject.data = {}; | |
fulfilledDependencies.forEach(function fulfilledDependenciesIterator(dependencyName) { | |
nodeLikeObject.data[dependencyName] = this[dependencyName].data; | |
}, this); | |
} | |
else { | |
nodeLikeObject.errors = {}; | |
fulfilledDependencies.forEach(function (dependencyName) { | |
nodeLikeObject.errors[dependencyName] = this[dependencyName].error; | |
}, this); | |
} | |
node.run(nodeLikeObject); | |
} | |
}, | |
/** | |
A flow basically is a graph which implies that the garbage collector will not be able | |
to clear this objects if they are no longer needed by the developer. | |
this method takes care of clearing those references and gives the developer full control | |
of when to discard the flow. | |
@property destroy <public> [Function] | |
**/ | |
destroy: function destroy() { | |
this.children.forEach(function (child) { | |
child.destroy(); | |
}); | |
return null; | |
}, | |
/** | |
This is a utility method to introspect a flow. flow use case is for when a complex sequence | |
needs to be solved and its very hard to solve it by simpler programming constructs, so mapping | |
this sequences is hard, this method dumps the flow into a dot directed graph to be visualized. | |
@property toDot <public> [Function] | |
@return string The actual dot string that represents this flow graph. | |
**/ | |
toDot: function toDot() { | |
var dotGraph = []; | |
dotGraph.push("digraph " + this.name + " {"); | |
this.children.forEach(function (step) { | |
dotGraph.push(step.name); | |
step.dependencies.forEach(function (dependencyName) { | |
dotGraph.push(dependencyName + " -> " + step.name); | |
}); | |
}); | |
dotGraph.push("}"); | |
console.debug("Put this in a file and run this in a terminal: dot -Tsvg yourDotFile > graph.svg"); | |
return dotGraph.join("\n"); | |
} | |
} | |
}); | |
Class('FlowNode').includes(NodeSupport)({ | |
prototype: { | |
name: '', | |
data: undefined, | |
error: undefined, | |
isFulfilled: false, | |
isRejected: false, | |
startTime: undefined, | |
code: function () { throw 'no code'; }, | |
errorCode: undefined, | |
init: function (config) { | |
Object.keys(config).forEach(function (property) { | |
this[property] = config[property]; | |
}, this); | |
}, | |
run: function (nodeLikeObject) { | |
var node = this; | |
this.startTime = new Date(); | |
if (nodeLikeObject.hasOwnProperty('data') === true) { | |
setTimeout(function runFlowNode() { | |
node.code(nodeLikeObject); | |
}, 0); | |
} | |
else if (typeof this.errorCode !== 'undefined') { | |
setTimeout(function runFlowNodeError() { | |
node.errorCode(nodeLikeObject); | |
}, 0); | |
} | |
}, | |
fulfill: function (data) { | |
this.data = data; | |
this.isFulfilled = true; | |
var executionTime = (new Date() - this.startTime)/1000; | |
console.log(`STEP SUCCESS: ${this.name} took ${executionTime}s`); | |
if (this.parent === null) { | |
return; | |
} | |
this.parent.dispatch(this.name); | |
}, | |
reject: function (error) { | |
this.error = error; | |
this.isRejected = true; | |
var executionTime = (new Date() - this.startTime)/1000; | |
console.log(`STEP FAILED: ${this.name} took ${executionTime}`); | |
if (this.parent === null) { | |
return; | |
} | |
this.parent.dispatch(this.name); | |
this.parent.dispatch('reject', { | |
data: { node: this, error: error } | |
}); | |
}, | |
destroy: function destroy() { | |
this.parent.removeChild(this); | |
return null; | |
} | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment