Closed tunnckoCore closed 9 years ago
updates: NOW BOTH ARE ABSOLUTELY ROBUST AND AWESOME
'use strict'
var co = require('co')
var assert = require('assert')
var nal = require('now-and-later')
var extend = require('extend-shallow')
var asyncDone = require('async-done')
var handleArguments = require('handle-arguments')
var isGenFn = require('is-es6-generators').fn
function Berck (context) {
if (!(this instanceof Berck)) {
return new Berck(context)
}
this.ctx = context || {}
this.fns = []
return this
}
Berck.prototype.use = function (fn) {
if (fn instanceof Berck) {
return this.use(fn.fns)
}
if (Array.isArray(fn)) {
var len = fn.length
var i = 0
while (i < len) {
this.use(fn[i++])
}
return this
}
assert(typeof fn === 'function', 'middleware should be function')
this.fns.push(fn)
return this
}
function run (method) {
return function () {
var argz = handleArguments(arguments)
var ctx = this.ctx
argz.args.forEach(function (arg) {
if (!Array.isArray(arg) && typeof arg === 'object') {
ctx = extend({}, ctx, arg)
}
})
nal[method](this.fns, function (fn, next) {
if (isGenFn(fn)) {
fn = co.wrap(fn)
}
fn = fn.bind(ctx)
asyncDone(fn, next)
}, {}, argz.callback)
}
}
Berck.prototype.run = run('mapSeries')
Berck.prototype.series = run('mapSeries')
Berck.prototype.parallel = run('map')
var fs = require('fs')
var assert = require('assert')
var wait = require('co-wait')
var mzfs = require('mz/fs')
var thunky = require('always-thunk')
var readFile = thunky(fs.readFile)
var app = new Berck({foo: 'bar'})
app
.use(function callbackFn (next) {
var self = this
setTimeout(function () {
self.data = true
console.log(1, self)
next(null, 123)
}, Math.random() * 2000)
})
.use(function callbackThunk (next) {
assert(this.data === true)
console.log(2, this)
this.data = 345
readFile('./pkg.json', 'utf8')(next)
})
.use(function promiseFn () {
assert(this.data === 345)
console.log(3, this)
this.data = 'foo'
return Promise.resolve(555)
})
.use(function * promiseGen () {
yield wait(Math.random() * 10000)
assert(this.data === 'foo')
var content = yield mzfs.readFile('./pkg.json', 'utf8')
console.log(4, this)
this.data = 'hello'
return content
})
.use(function * genThunk () {
assert(this.data === 'hello')
console.log(5, this)
return yield readFile('./pkg.json', 'utf8')
})
// .series({baz: 'qux'}, {hello: 'world'}, console.log)
/** kock - control flow *******************
******************************************/
var handleCallback = require('handle-callback')
var handleArguments = require('handle-arguments')
var manageArguments = require('manage-arguments')
var alwaysPromise = require('always-promise')
var asyncDone = require('async-done')
function arrayify (val) {
return !Array.isArray(val) ? [val] : val
}
function bindArray (fn, thisArg, args) {
return fn.bind.apply(fn, [thisArg].concat(args))
}
function kock (fn) {
fn = arrayify(fn)
function lastCallback (ctx, done) {
return function lastCb_ () {
var args = manageArguments(arguments)
var err = args[0]
var results = args.slice(1).shift()
if (kock.lastOnly === true || kock.lastonly === true) {
var last = results[results.length - 1]
return done.call(ctx, err, last)
}
if (kock.unflatten === true || kock.flatten === false) {
return done.apply(ctx, [err].concat(results))
}
return done.call(ctx, err, results)
}
}
return function (cb) {
var argz = handleArguments(arguments)
var self = this || {}
var results = []
var i = 0
self._ = argz.arguments
nal.mapSeries(fn, function (_fnc, next) {
_fnc = isGenFn(_fnc) ? co.wrap(_fnc) : _fnc
_fnc = i === 0 ? bindArray(_fnc, self, argz.args) : bindArray(_fnc, self, results.shift())
asyncDone(_fnc, function (err) {
i++
if (err) {
return next(err)
}
var args = manageArguments(arguments).slice(1)
results.push(args)
next.apply(null, [null].concat(args))
})
}, lastCallback(self, argz.callback))
}
}
// kock(function callbackFn (fp, next) {
// return fs.readFile(fp, 'utf8', next)
// })('./pkg.json', console.log)
// kock(function callbackThunk (fp, next) {
// readFile(fp, 'utf8')(next)
// })('./pkg.json', console.log)
// kock(function promiseFn (val) {
// return Promise.resolve(123 + val)
// })(123, console.log) //=> 246
// kock(function * promiseGen (fp, next) {
// return yield mzfs.readFile(fp, 'utf8')
// })('./pkg.json', console.log)
// var ko = kock([
// function * genThunk (fp, next) {
// console.log(this)
// return yield readFile(fp, 'utf8')
// },
// function promiseFn (fp) {
// console.log(this)
// return Promise.resolve(123 + fp)
// }
// ])
// ko.call({foo: 'bar'}, './pkg.json', console.log)
// var cheerio = require('cheerio')
// var got = require('then-got')
// kock.unflatten = true
// kock([
// function (hello) {
// this.hello = this._[0] // this._ is all given arguments as array
// this.start = 123
// return got('http://lapwinglabs.com')
// },
// function (lapwinglabs, callback) {
// var self = this
// var body = lapwinglabs[0]
// var $ = cheerio.load(body)
// this.title = $('title').text()
// callback(null, this.title, 555)
// },
// function (lapwingTitle, num) {
// assert(this.title === lapwingTitle) // Lapwing Labs
// assert(this.hello === 'hello world')
// return got('http://leveredreturns.com')
// }
// ])('hello world', {foo: 'bar'}, function (er, lapwinglabs, lapwingTitle, leveredreturns) {
// // [0] is body, [1] is stream
// console.log(er)
// console.log(lapwinglabs[1].req._headers)
// console.log(lapwingTitle)
// console.log(leveredreturns[1].req._headers)
// console.log(this)
// console.log('end')
// })
// var got = require('got')
// var cheerio = require('cheerio')
// function get (url, cb) {
// var self = this
// got(url, function (err, body, stream) {
// if (err) {
// return cb(err)
// }
// self.foo = 123
// cb(null, body)
// })
// }
// function title (text, cb) {
// this.bar = 456
// var $ = cheerio.load(text)
// cb(null, $('title').text())
// }
// // kock.unflatten = true
// // kock.flatten = false
// // kock.lastonly = true
// // kock.lastOnly = true
// kock([get, title])('http://lapwinglabs.com', function (err, res) {
// console.log(this) // {foo: 123, bar: 456}
// console.log(err, res)
// console.log('end')
// })
need tests
/*!
* kock <https://github.com/tunnckoCore/kock>
*
* Copyright (c) 2015 Charlike Mike Reagent <@tunnckoCore> (http://www.tunnckocore.tk)
* Released under the MIT license.
*/
'use strict'
var util = require('util')
var kindOf = require('kind-of')
var Options = require('option-cache')
var isGenFn = require('is-es6-generator-function')
var isPromise = require('is-promise')
var asyncDone = require('async-done')
var nowAndLater = require('now-and-later')
var handleArguments = require('handle-arguments')
var manageArguments = require('manage-arguments')
/**
* utils
*/
function bindify (fn, thisArg, args) {
return fn.bind.apply(fn, [thisArg].concat(args))
}
function arrayify (val) {
return !Array.isArray(val) ? [val] : val
}
function wrapify (promise) {
return function () {
return promise
}
}
/**
* Expose `Kock`
*/
module.exports = Kock
/**
* Create a new instance of `Kock`.
*
* @param {Object} `options` Initialize with default options.
* @api public
*/
function Kock (options) {
if (!(this instanceof Kock)) {
return new Kock(options)
}
Options.call(this, options)
this._defaultOptions()
}
util.inherits(Kock, Options)
Kock.prototype._defaultOptions = function () {
if (!this.hasOption('context')) {
this.option('context', {})
}
if (!this.hasOption('done') || kindOf(this.option('done')) !== 'function') {
this.option('done', function noop () {})
}
if (!this.hasOption('promise') || !this.isBoolean('promise')) {
this.option('promise', false)
}
if (!this.hasOption('flatten') || !this.isBoolean('flatten')) {
this.option('flatten', true)
}
if (!this.hasOption('onlylast') || !this.isBoolean('onlylast')) {
this.option('onlylast', false)
}
if (!this.hasOption('promisify') || kindOf(this.option('promisify')) !== 'function') {
this.option('promisify', require('hybridify'))
}
if (!this.hasOption('generatorify') || kindOf(this.option('generatorify')) !== 'function') {
this.option('generatorify', require('co').wrap)
}
return this
}
Kock.prototype.series = factory('mapSeries')
Kock.prototype.parallel = factory('map')
function factory (method) {
return function _factory_ (fns, extensions) {
if (kindOf(fns) === 'function') {
fns = [fns]
}
if (kindOf(fns) !== 'array' && kindOf(fns) !== 'object') {
throw new TypeError('kock: series expect `fns` to be array or an object')
}
var self = this
extensions = this.option('extensions', extensions)
function complete () {
var argz = handleArguments(arguments)
var ctx = self.option('context')
var results = []
var index = 0
function iterator (fn, next) {
var res = index === 0 ? argz.args : results.shift()
fn = isPromise(fn) ? wrapify(fn) : fn
fn = isGenFn(fn) ? self.option('generatorify')(fn) : fn
fn = bindify(fn, ctx, res)
asyncDone(fn, function (err, res) {
index++
if (err) {
return next(err)
}
var args = manageArguments(arguments).slice(1)
results.push(args)
next.apply(null, [null].concat(args))
})
}
nowAndLater[method](fns, iterator, extensions, lastCallback(ctx, argz.callback))
}
function lastCallback (ctx, done) {
done = done || self.option('done')
return function lastCb () {
var args = manageArguments(arguments)
var err = args[0]
var res = args.slice(1).shift()
var last = res[res.length - 1]
if (self.enabled('flatten') && fns.length === 1) {
self.disable('flatten')
}
if (self.enabled('onlylast')) {
return done.call(ctx, err, last)
}
if (self.disabled('flatten')) {
return done.apply(ctx, [err].concat(res))
}
return done.call(ctx, err, res)
}
}
if (this.enabled('promise')) {
return this.option('promisify')(complete)
}
return complete
}
}
Berck and Kock, almost ready