├── .gitignore ├── .npmignore ├── History.md ├── Makefile ├── Readme.md ├── index.js ├── package.json └── test └── stroll.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | support 2 | test 3 | examples 4 | *.sock 5 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 2.0.0 / 2015-09-09 3 | ================== 4 | 5 | * add support for deep keys (ex. a.0.k) 6 | 7 | 1.0.0 / 2010-01-03 8 | ================== 9 | 10 | * Initial release 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | test: 3 | @./node_modules/.bin/mocha \ 4 | --reporter spec 5 | 6 | .PHONY: test 7 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 2 | # stroll 3 | 4 | asynchronously & recursively walk through an object, array or literal. 5 | 6 | uses [wrapped](https://github.com/matthewmueller/wrapped) to provide generator and promise support. 7 | 8 | ## installation 9 | 10 | ``` 11 | npm install stroll 12 | ``` 13 | 14 | ## Usage 15 | 16 | ```js 17 | var stroll = require('stroll') 18 | 19 | var obj = { 20 | a: 'a', 21 | b: ['b0', 'b1'], 22 | c: 'c', 23 | d: { 24 | e: 'e', 25 | f: 'f' 26 | } 27 | } 28 | 29 | stroll(obj, function(v, k, next) { 30 | setTimeout(function() { 31 | next(null, v + ' ' + v) 32 | }, 50) 33 | }, function(err, obj) { 34 | if (err) throw err; 35 | assert.deepEqual(obj, { 36 | a: 'a a', 37 | b: ['b0 b0', 'b1 b1'], 38 | c: 'c c', 39 | d: { 40 | e: 'e e', 41 | f: 'f f' 42 | } 43 | }) 44 | }) 45 | ``` 46 | 47 | ## API 48 | 49 | ##### `stroll(value, fn, done)` 50 | 51 | recursively stroll through `value`, calling `fn` on each literal. 52 | `done` is called when all functions have finished. 53 | 54 | ## License 55 | 56 | (The MIT License) 57 | 58 | Copyright (c) 2015 Matthew Mueller <matt@lapwinglabs.com> 59 | 60 | Permission is hereby granted, free of charge, to any person obtaining 61 | a copy of this software and associated documentation files (the 62 | 'Software'), to deal in the Software without restriction, including 63 | without limitation the rights to use, copy, modify, merge, publish, 64 | distribute, sublicense, and/or sell copies of the Software, and to 65 | permit persons to whom the Software is furnished to do so, subject to 66 | the following conditions: 67 | 68 | The above copyright notice and this permission notice shall be 69 | included in all copies or substantial portions of the Software. 70 | 71 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 72 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 73 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 74 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 75 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 76 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 77 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 78 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module Dependencies 3 | */ 4 | 5 | var is_object = require('is-object') 6 | var foreach = require('foreach') 7 | var wrapped = require('wrapped') 8 | var Batch = require('batch') 9 | var is_array = Array.isArray 10 | var noop = function () {} 11 | 12 | /** 13 | * Export `stroll` 14 | */ 15 | 16 | module.exports = stroll 17 | 18 | /** 19 | * Initialize `stroll` 20 | * 21 | * @param {Mixed} value 22 | * @param {Function} fn 23 | * @param {Function} done 24 | */ 25 | 26 | function stroll (value, fn, done, key) { 27 | var wrap = wrapped(fn) 28 | var batch = Batch() 29 | var out 30 | 31 | if (is_object(value)) { 32 | out = {} 33 | foreach(value, function (v, k) { 34 | batch.push(function (next) { 35 | stroll(v, fn, function(err, value) { 36 | if (err) return next(err) 37 | out[k] = value 38 | next() 39 | }, prop(key, k)) 40 | }) 41 | }) 42 | } else if (is_array(value)) { 43 | out = [] 44 | foreach(value, function(v, k) { 45 | batch.push(function (next) { 46 | stroll(v, fn, function(err, value) { 47 | if (err) return next(err) 48 | out[k] = value 49 | next() 50 | }, prop(key, k)) 51 | }) 52 | }) 53 | } else { 54 | out = null 55 | batch.push(function (next) { 56 | wrap(value, key, function(err, v) { 57 | if (err) return next(err) 58 | out = v 59 | next() 60 | }) 61 | }) 62 | } 63 | 64 | function run (fn) { 65 | batch.end(function (err, v) { 66 | if (err) return fn(err) 67 | return fn(null, out) 68 | }) 69 | } 70 | 71 | // run if we have a callback 72 | done && run(done) 73 | 74 | // thenable 75 | stroll.then = function (fulfill, reject) { 76 | run(function(err, value) { 77 | err ? reject(err) : fulfill(value) 78 | }) 79 | } 80 | 81 | return stroll 82 | } 83 | 84 | function prop (base, prop) { 85 | if (!base) return prop 86 | return base + '.' + prop 87 | } 88 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stroll", 3 | "version": "2.0.0", 4 | "description": "recursively walk an object or array with async support", 5 | "keywords": ["walk", "async", "recursive", "map"], 6 | "author": "Matthew Mueller ", 7 | "repository": { 8 | "type": "git", 9 | "url": "git://github.com/LapwingLabs/stroll.git" 10 | }, 11 | "dependencies": { 12 | "batch": "^0.5.2", 13 | "foreach": "^2.0.5", 14 | "is-object": "^1.0.1", 15 | "wrapped": "^1.0.0" 16 | }, 17 | "devDependencies": { 18 | "mocha": "^2.2.5", 19 | "should": "*" 20 | }, 21 | "main": "index" 22 | } 23 | -------------------------------------------------------------------------------- /test/stroll.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module Dependencies 3 | */ 4 | 5 | var assert = require('assert') 6 | var stroll = require('..') 7 | 8 | /** 9 | * Tests 10 | */ 11 | 12 | describe('objects', function() { 13 | it('should work with objects', function(done) { 14 | var obj = { 15 | a: 'a', 16 | b: 'b', 17 | c: 'c', 18 | d: { 19 | e: 'e', 20 | f: 'f' 21 | } 22 | } 23 | 24 | stroll(obj, function(v, k, next) { 25 | v == 'a' && assert.equal(k, 'a') 26 | v == 'e' && assert.equal(k, 'd.e') 27 | v == 'd' && assert.equal(k, 'd') 28 | setTimeout(function() { 29 | next(null, v + v) 30 | }, 50) 31 | }, function(err, obj) { 32 | if (err) return done(err) 33 | assert.deepEqual(obj, { 34 | a: 'aa', 35 | b: 'bb', 36 | c: 'cc', 37 | d: { 38 | e: 'ee', 39 | f: 'ff' 40 | } 41 | }) 42 | done() 43 | }) 44 | }) 45 | 46 | it('should pass errors through', function(done) { 47 | var obj = { 48 | a: 'a', 49 | b: 'b', 50 | c: 'c' 51 | } 52 | 53 | stroll(obj, function(v, k, next) { 54 | setTimeout(function() { 55 | if (v == 'b') { 56 | next(new Error('error on b')) 57 | } else { 58 | next(null, v + v) 59 | } 60 | }, 50) 61 | }, function(err, obj) { 62 | assert.equal(err.message, 'error on b') 63 | assert.ok(!obj) 64 | done() 65 | }) 66 | }) 67 | 68 | it('should support passing errors back into the object', function(done) { 69 | var obj = { 70 | a: 'a', 71 | b: 'b', 72 | c: 'c' 73 | } 74 | 75 | stroll(obj, function(v, k, next) { 76 | setTimeout(function() { 77 | next(null, new Error('error on ' + v)) 78 | }, 50) 79 | }, function(err, obj) { 80 | if (err) return done(err) 81 | assert.equal(obj.a.message, 'error on a') 82 | assert.equal(obj.b.message, 'error on b') 83 | assert.equal(obj.c.message, 'error on c') 84 | done() 85 | }) 86 | }) 87 | }) 88 | 89 | describe('arrays', function() { 90 | it('should work with arrays', function(done) { 91 | var arr = ['a', 'b', 'c'] 92 | 93 | stroll(arr, function(v, k, next) { 94 | setTimeout(function() { 95 | next(null, v + v) 96 | }, 50) 97 | }, function(err, arr) { 98 | if (err) return done(err) 99 | assert.deepEqual(arr, [ 100 | 'aa', 101 | 'bb', 102 | 'cc' 103 | ]) 104 | done() 105 | }) 106 | }) 107 | 108 | it('should pass errors through', function(done) { 109 | var arr = ['a', 'b', 'c'] 110 | stroll(arr, function(v, k, next) { 111 | setTimeout(function() { 112 | if (v == 'b') { 113 | next(new Error('error on b')) 114 | } else { 115 | next(null, v + v) 116 | } 117 | }, 50) 118 | }, function(err, arr) { 119 | assert.equal(err.message, 'error on b') 120 | assert.ok(!arr) 121 | done() 122 | }) 123 | }) 124 | 125 | it('should support passing errors back into the array', function(done) { 126 | var arr = ['a', 'b', 'c'] 127 | 128 | stroll(arr, function(v, k, next) { 129 | setTimeout(function() { 130 | next(null, new Error('error on ' + v)) 131 | }, 50) 132 | }, function(err, arr) { 133 | if (err) return done(err) 134 | assert.equal(arr[0].message, 'error on a') 135 | assert.equal(arr[1].message, 'error on b') 136 | assert.equal(arr[2].message, 'error on c') 137 | done() 138 | }) 139 | }) 140 | }) 141 | 142 | describe('literals', function() { 143 | it('should work with literals', function(done) { 144 | stroll(10, function(v, k, fn) { 145 | return fn(null, v + v) 146 | }, function(err, v) { 147 | if (err) return done(err) 148 | assert.equal(v, 20) 149 | done() 150 | }) 151 | }) 152 | 153 | it('should pass errors through', function(done) { 154 | stroll(10, function(v, k, fn) { 155 | return fn(new Error('10 is not okay'), v + v) 156 | }, function(err, v) { 157 | assert.equal(err.message, '10 is not okay') 158 | assert.ok(!v) 159 | done() 160 | }) 161 | }) 162 | 163 | it('should support passing errors back but not erroring out', function(done) { 164 | stroll(10, function(v, k, fn) { 165 | return fn(null, new Error('10 is not okay')) 166 | }, function(err, v) { 167 | if (err) return done(err) 168 | assert.equal(v.message, '10 is not okay') 169 | done() 170 | }) 171 | }) 172 | }) 173 | 174 | describe('mixed', function() { 175 | it('should work with mixed values', function(done) { 176 | var obj = { 177 | a: 'a', 178 | b: ['b0', 'b1'], 179 | c: 'c', 180 | d: { 181 | e: 'e', 182 | f: 'f' 183 | } 184 | } 185 | 186 | stroll(obj, function(v, k, next) { 187 | v == 'b0' && assert.equal(k, 'b.0') 188 | v == 'b1' && assert.equal(k, 'b.1') 189 | v == 'f' && assert.equal(k, 'd.f') 190 | 191 | setTimeout(function() { 192 | next(null, v + ' ' + v) 193 | }, 50) 194 | }, function(err, obj) { 195 | if (err) done(err); 196 | assert.deepEqual(obj, { 197 | a: 'a a', 198 | b: ['b0 b0', 'b1 b1'], 199 | c: 'c c', 200 | d: { 201 | e: 'e e', 202 | f: 'f f' 203 | } 204 | }) 205 | done() 206 | }) 207 | }) 208 | }) 209 | --------------------------------------------------------------------------------