-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit f91d373
Showing
5 changed files
with
215 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
Async tree traversal for nodejs | ||
=============================== | ||
|
||
***under development!*** | ||
|
||
<tt>tree-monkey</tt> takes a JSON or object literal tree of the form | ||
|
||
{ | ||
"nodeA": { | ||
"nodeAA": { | ||
"nodeAAA": {} | ||
} | ||
}, | ||
"nodeB": { | ||
"nodeBA": {} | ||
"nodeBB": {} | ||
} | ||
} | ||
|
||
and traversers it asynchronously. | ||
|
||
|
||
## Usage | ||
|
||
The basic usage is as follows: | ||
|
||
var monkey = require('tree-monkey') | ||
, tree = { ... }; | ||
|
||
monkey.preOrder(tree, function (node, path, callback) { | ||
// do something async with the current node and/or path | ||
... | ||
|
||
// signal that you are done | ||
callback(); | ||
}); | ||
|
||
### Pre-order traversal | ||
|
||
Visits all nodes depth-first in async pre-order, meaning each parent node is visited before its child nodes and then all child nodes are visited asynchronously. This means all branches are visited in their own speed, so to say, and we can't know the order in which they will complete. | ||
|
||
preOrder(tree, nodeFunction, callback) | ||
|
||
Arguments: | ||
|
||
<dt><tt>tree</tt></dt> <dd>the tree to traverse</dd> | ||
<dt><tt>nodeFunction</tt></dt> <dd>the function called on each node. Receives the current node, the current path and a callback as arguments. Callback *must* be called eventually.</dd> | ||
<dt><tt>callback</tt></dt> <dd>an optional callback, which is called after the whole tree has been traversed</dd> | ||
|
||
### Post-order traversal | ||
|
||
Visits all nodes depth-first in async prost-order, meaning children will be visited before their parent node. Child nodes are visited asynchronously, meaning we can't know the order in which they will complete. | ||
|
||
preOrder(tree, nodeFunction, callback) | ||
|
||
Take the same arguments as <tt>preOrder</tt> |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
{ | ||
"name": "tree-monkey", | ||
"version": "0.0.1", | ||
"description": "Async tree traversal for nodejs", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "node_modules/jasmine-node/bin/jasmine-node --coffee specs/" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/felixhageloh/tree-monkey.git" | ||
}, | ||
"keywords": [ | ||
"async", | ||
"tree", | ||
"traversal", | ||
"directory", | ||
"path" | ||
], | ||
"author": "Felix Hageloh", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/felixhageloh/tree-monkey/issues" | ||
}, | ||
"dependencies": { | ||
"async": "~0.2.9" | ||
}, | ||
"devDependencies": { | ||
"coffee-script": "~1.6.3", | ||
"jasmine-node": "~1.11.0" | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
monkey = require '../src/tree-monkey' | ||
|
||
describe 'traverse tree', -> | ||
tree = { | ||
'a': { 'aa': {} } | ||
'b': { | ||
'ba': {}, | ||
'bb': { 'bba': {} } | ||
} | ||
} | ||
|
||
allPaths = ['', '/a', '/b', '/b/ba', '/b/bb', '/a/aa', '/b/bb/bba'] | ||
|
||
treeFunction = null | ||
|
||
beforeEach -> | ||
# simulate async with a random timeout between 1ms and 100ms | ||
treeFunction = jasmine.createSpy().andCallFake (node, path, callback) -> | ||
setTimeout callback, Math.round(Math.random() * 100) | ||
|
||
it 'should traverse a tree pre order', -> | ||
done = false | ||
|
||
runs -> | ||
monkey.preOrder tree, treeFunction, -> done = true | ||
waitsFor -> done | ||
|
||
runs -> | ||
expect(treeFunction.calls.length).toBe allPaths.length | ||
|
||
# collect all paths in the order they were called | ||
paths = [] | ||
paths.push call.args[1] for call in treeFunction.calls | ||
|
||
# make sure every single path has been visited | ||
for path in allPaths | ||
index = paths.indexOf(path) | ||
console.log path unless index > -1 | ||
expect(index).toBeGreaterThan(-1) | ||
|
||
# check that pre-order is maintained, i.e parent nodes are | ||
# called before their children | ||
expect(paths.indexOf('')).toBeLessThan paths.indexOf('/a') | ||
expect(paths.indexOf('')).toBeLessThan paths.indexOf('/b') | ||
expect(paths.indexOf('/a')).toBeLessThan paths.indexOf('/a/aa') | ||
expect(paths.indexOf('/b')).toBeLessThan paths.indexOf('/b/ba') | ||
expect(paths.indexOf('/b')).toBeLessThan paths.indexOf('/b/bb') | ||
expect(paths.indexOf('/b/bb')).toBeLessThan paths.indexOf('/b/bb/bba') | ||
|
||
it 'should traverse a tree post order', -> | ||
done = false | ||
|
||
runs -> | ||
monkey.postOrder tree, treeFunction, -> done = true | ||
waitsFor -> done | ||
|
||
runs -> | ||
expect(treeFunction.calls.length).toBe allPaths.length | ||
|
||
# collect all paths in the order they were called | ||
paths = [] | ||
paths.push call.args[1] for call in treeFunction.calls | ||
|
||
# make sure every single path has been visited | ||
for path in allPaths | ||
index = paths.indexOf(path) | ||
console.log path unless index > -1 | ||
expect(index).toBeGreaterThan(-1) | ||
|
||
# check that post-order is maintained, i.e parent nodes are | ||
# called after their children | ||
expect(paths.indexOf('')).toBeGreaterThan paths.indexOf('/a') | ||
expect(paths.indexOf('')).toBeGreaterThan paths.indexOf('/b') | ||
expect(paths.indexOf('/a')).toBeGreaterThan paths.indexOf('/a/aa') | ||
expect(paths.indexOf('/b')).toBeGreaterThan paths.indexOf('/b/ba') | ||
expect(paths.indexOf('/b')).toBeGreaterThan paths.indexOf('/b/bb') | ||
expect(paths.indexOf('/b/bb')).toBeGreaterThan paths.indexOf('/b/bb/bba') | ||
|
||
it 'should not require a final callback', -> | ||
expect( -> | ||
monkey.postOrder tree, (_, __, callback) -> callback() | ||
).not.toThrow() |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
async = require 'async' | ||
|
||
preOrderTraverser = (visitNode) -> | ||
visitBranch = (node, path, callback) -> | ||
async.series [ | ||
visitNode(node, path), | ||
visitChildren(node, path, visitBranch) | ||
], callback | ||
|
||
postOrderTraverser = (visitNode) -> | ||
visitBranch = (node, path, callback) -> | ||
async.series [ | ||
visitChildren(node, path, visitBranch), | ||
visitNode(node, path) | ||
], callback | ||
|
||
visitChildren = (node, path, visitBranch) -> (callback) -> | ||
visitorFunctions = [] | ||
|
||
for name, child of node | ||
do (name, child) -> | ||
visitorFunctions.push (done) -> visitBranch child, path+'/'+name, done | ||
|
||
async.parallel visitorFunctions, callback | ||
|
||
# care for some curry with that? | ||
visitNode = (nodeFunction) -> (node, path) -> (callback) -> | ||
nodeFunction node, path, callback | ||
|
||
|
||
# visits all nodes depth-first in async pre-order, meaning each parent node | ||
# is visited before its child nodes and then all child nodes are visited | ||
# asynchrounously. This means all branches are visited in their own speed, | ||
# so to say, and we can't know the order in which they will complete. | ||
exports.preOrder = (rootNode, nodeFunction, callback) -> | ||
preOrderTraverser(visitNode(nodeFunction))(rootNode, '', callback) | ||
|
||
|
||
# visits all nodes depth-first in async prost-order, meaning children will | ||
# be visited before their parent node. Child nodes are visited | ||
# asynchrounously, meaning we can't know the order in which they will | ||
# complete. | ||
exports.postOrder = (rootNode, nodeFunction, callback) -> | ||
postOrderTraverser(visitNode(nodeFunction))(rootNode, '', callback) |