tunnckoCore / blankr

:heart: tasks, todos, ideas, streaks, WIP, etc
https://i.am.charlike.online
4 stars 1 forks source link

javascript hybrids #48

Open tunnckoCore opened 8 years ago

tunnckoCore commented 8 years ago

// always-generator
// always-done
// always-thunk
// always-promise
// coone - direct copy of `co@3`
// voa - dirct copy of `co@4`
// merz

// alwaysGenerator(mixed) -> *(foo, bar, baz)
// 1) yield *(foo, bar, baz) -> result
// 2) *(foo, bar, baz) -> promise.then()
//
// alwaysDone(mixed, foo, bar, baz, callback)
// coone(mixed, foo, bar, baz) -> thunk(callback)
// voa(mixed, foo, bar, baz) -> promise.then()
//
// merz(mixed) -> fn(foo, bar, baz, callback)
// alwaysThunk(mixed) -> fn(foo, bar, baz) -> thunk(callback)
// alwaysPromise(mixed) -> fn(foo, bar, baz) -> promise.then()
tunnckoCore commented 8 years ago

new

// letta - co@4 simplest
// voa - co@4 on top of always-done
// merz - similiar to co@4 and voa/letta, on top of always-callback/always-done
// always-promise - executes and returns promise
// always-done - co@4 - handle additional things
// always-callback - on top of letta -> always-done, but callbacks
// always-thunk - similiar to co@3 style, strictly accepts only callback
// coone - co@3

// letta               (fn, foo, bar, baz)-> promise.then()
// voa                 (fn, foo, bar, baz, callback)
// alwa                (fn, foo, bar, baz)-> thunk(callback)
//
// alwaysDone          (val, foo, bar, baz)-> promise.then()
// coone               (val, foo, bar, baz)-> thunk(callback)
// alwaysCallback      (val, foo, bar, baz, callback)
//
// alwaysPromise       (val)-> fn(foo, bar, baz)-> promise.then()
// alwaysThunk         (val)-> fn(foo, bar, baz)-> thunk(callback)
// merz                (val)-> fn(foo, bar, baz, callback)
// redolent            (fn)-> fn(foo, bar, baz[, callback]) -> promise.then()
//
// alwaysGenerator     (val)-> fn*(foo, bar, baz)-> promise.then()
tunnckoCore commented 8 years ago

additional descs


// always-done
// always-thunk - thunkify everything
// always-promise - promisify everything
// always-generator - generatorify everything
// always-callback - pass 100% of `async-done`
// then-callback - accepts promise and returns promis with modified `.then`
// make-callback - creates callback api fro sync fn, but throws if not sync
// create-callback - creates callback api for sync fn, but dont throws if async
// handle-callback - accepts promise and callback, return promise
// handle-errors
// handle-arguments - handles `arguments` object and return object with `args` and `cb`
// manage-arguments - similiar to `sliced` and `array-slice`
// bind-arguments - bind context (optional) and multiple arguments to function
// bind-context - bind context to the given function and preserves her name.
// useware-context - passing custom context
// useware - useful for `.use` methods
// merz - pass 100% of `co@4` and adds generators to always-done, changes signature
// benz - adds options and `.series/.parallel` and `.compose` methods
// vez - on top of `benz`, middleware composition at new level
// voa
// voala - testing runner on top of vez/merz/benz
// coone - cpass 100% of `co@3`
// letta - accepts only sync/async/callback functions, executes and return promise
// letta-value - extends `letta` by adding handling of streams, child processes, promises
// redolent - promisify sync/async/callback function
// native-promise - gets native promise or falsey value if it native promise not available
// native-or-another - always get promise, native or bluebird/custom only when needed
// hybridify - classic hybrid, use callback and promise api in same time
// hybridify-all - hybridify objects
tunnckoCore commented 8 years ago

temp

function alwaysDone (val) {
  var lettaValue = require('letta-value')
  if (typeof val === 'function') {
    return require('letta').apply(this, arguments).then(lettaValue)
  }
  return lettaValue(val)
}

function alwaysCallback (val) {
  var argz = require('handle-arguments')(arguments)
  if (!argz.callback) {
    throw new TypeError('always-callback: expect last argument to be callback function')
  }
  var promise = require('always-done').apply(this, [val].concat(argz.args))
  require('then-callback')(promise).then(argz.callback)
}

function alwaysPromise (val) {
  var self = this
  return function promisify () {
    var args = require('sliced')(arguments)
    return require('always-done').apply(self || this, [val].concat(args))
  }
}

function alwaysGenerator (val) {
  var self = this
  return function generatorify () {
    var ctx = self || this
    var args = require('sliced')(arguments)
    return function * alwaysGeneratorFunction () {
      return require('always-done').apply(this, [val].concat(args))
    }
  }
}

function alwaysThunk (val) {
  var self = this
  return function thunkify () {
    var ctx = self || this
    var args = require('sliced')(arguments)
    return function (done) {
      var promise = require('always-done').apply(ctx || this, [val].concat(args))
      return require('then-callback')(promise).then(done)
    }
  }
}

function merz (val) {
  var self = this
  return function () {
    var args = require('sliced')(arguments)
    require('always-callback').apply(self || this, [val].concat(args))
  }
}

function coone (val) {
  var self = this
  var args = require('sliced')(arguments, 1)
  return function (done) {
    require('merz').call(self || this, val).apply(self || this, args.concat(done))
  }
}

/**
 * Follow `Don't Repeat Yourself`.
 *
 * Handles if given value is Stream, Child Process,
 * RX Observable, Error or just a value of sync function
 */

// @todo `letta-value` or `handle-value`
// to handles different values and always returning promise

function lettaValue (val) {
  if (isNodeStream(val) || isChildProcess(val)) {
    return letta(onStreamEnd, streamExhaust(val))
  }
  if (val && typeof val.subscribe === 'function') {
    if (val.value) return letta(function () {
      return val.value
    })
    return letta(subscribe, val)
  }
  if (isError(val)) {
    return letta(function () {
      throw val
    })
  }
  return letta(function () {
    return val
  })
}

/**
 * Callback-style wrapper for `rx.subscribe`
 */

function subscribe (val, each, callback) {
  if (typeof val !== 'object' && arguments.length <= 1) {
    throw new TypeError('expect `val` to be object / expect atleast 2 arguments')
  }
  callback = arguments.length === 2 ? each : callback
  each = arguments.length === 3 ? each : function noop () {}
  val.subscribe(each, callback, function onComplete () {
    callback.apply(this, [null].concat(sliced(arguments)))
  }.bind(this))
}
tunnckoCore commented 8 years ago

and the new way :lollipop: redike, redike-all, updated redolent, plus exampels

// @package `redike`
function redike (fn) {
  var Prome = require('native-or-another')(redike.promise)
  if (typeof fn !== 'function') {
    return Prome.reject(new TypeError('redike expect a function'))
  }
  // var utils = require('./utils')
  var argz = require('handle-arguments')(arguments)
  var self = this
  argz.args = argz.args.slice(1)

  if (argz.callback && !require('is-async-function')(argz.callback)) {
    argz.args = argz.args.concat(argz.callback)
  }

  var promise = new Prome(function prome (resolve, reject) {
    var isAsync = require('is-async-function')(fn)
    if (isAsync) {
      argz.args = argz.args.concat(function cb (err, res) {
        if (err) return reject(err)
        if (arguments.length > 2) res = require('sliced')(arguments, 1)
        resolve(res)
      })
    }
    var syncResult = fn.apply(self, argz.args)
    if (!isAsync) {
      resolve(syncResult)
    }
  })

  promise.Prome = Prome
  promise.___customPromise = Prome.___customPromise
  promise.___bluebirdPromise = Prome.___bluebirdPromise
  return promise
}

// @package `redike-all`
function redikeAll (val) {
  redike.promise = redikeAll.promise
  var args = require('sliced')(arguments)
  if (typeof val !== 'function') {
    return redike.apply(this, [function () {
      if (Array.isArray(args) && args.length === 1) {
        return args[0]
      }
      return args
    }])
  }
  return redike.apply(this, args)
}

// @package `redolent`
function redolent (val, Prome) {
  var self = this
  return function promisifyFn () {
    var ctx = self || this
    var args = require('sliced')(arguments)
    redikeAll.promise = Prome || redolent.promise || promisifyFn.promise
    return redikeAll.apply(ctx, [val].concat(args))
  }
}

var fs = require('fs')

// redike({a: 'b'}).then(console.log, console.error)                         // => err: redike expect a function
// redike(123, 'foo', {a: 'b'}).then(console.log, console.error)             // => err: redike expect a function
// redike(fs.readFile, './foo.js').then(console.log, console.error)          // => Buffer
// redike(fs.readFileSync, './foo.js').then(console.log, console.error)      // => Buffer
// redike(fs.readFile, 'not exist').then(console.log, console.error)         // => err: ENOENT
// redike(fs.readFileSync, 'not exist').then(console.log, console.error)     // => err: ENOENT

// redikeAll({a: 'b'}).then(console.log, console.error)                      // => {a: 'b'}
// redikeAll(123, 'foo', {a: 'b'}).then(console.log, console.error)          // => [123, 'foo', {a: 'b'}]
// redikeAll(fs.readFile, './foo.js').then(console.log, console.error)       // => Buffer
// redikeAll(fs.readFileSync, './foo.js').then(console.log, console.error)   // => Buffer
// redikeAll(fs.readFile, 'not exist').then(console.log, console.error)      // => err: ENOENT
// redikeAll(fs.readFileSync, 'not exist').then(console.log, console.error)  // => err: ENOENT

// redolent({a: 'b'})().then(console.log, console.error)                     // => {a: 'b'}
// redolent({a: 'b'})(234).then(console.log, console.error)                  // => [{a: 'b'}, 234]
// redolent(123, 'foo', {a: 'b'})().then(console.log, console.error)         // => [123, 'foo', {a: 'b'}]
// redolent('bar', 123)(555).then(console.log, console.error)                // => ['bar', 123, 555]
// redolent(fs.readFile)('./foo.js').then(console.log, console.error)        // => Buffer
// redolent(fs.readFileSync)('./foo.js').then(console.log, console.error)    // => Buffer
// redolent(fs.readFile)('not exist').then(console.log, console.error)       // => err: ENOENT
// redolent(fs.readFileSync)('not exist').then(console.log, console.error)   // => err: ENOENT
tunnckoCore commented 8 years ago

.series, .parallel, .settleSeries, .settleParallel aka hybridables/benz

'use strict'

var fs = require('fs')
var async = require('async')
var letta = require('letta')
var settle = false

async.map([
  function test1 () {
    return 123
  },
  function test2 () {
    throw new Error('foo')
  },
  function test3 (cb) {
    cb(null, 456)
  },
  function test4 (cb) {
    fs.readFile('packfdsf', cb)
  }

], function (fn, next) {
  var nextSettle = settle ? function settle (err) {
    next(null, err)
  } : next

  letta(fn, nextSettle).then(function (res) {
    next(null, res)
  }, nextSettle)
}, function done (err, res) {
  console.log('err', err)
  console.log('res', res)
})
tunnckoCore commented 8 years ago

The New Benz

'use strict'

var control = require('async')
var letta = require('letta')

function Benz (options) {
  if (!(this instanceof Benz)) {
    return new Benz(options)
  }
  this.options = typeof options === 'object' ? options : {settle: false}
}

Benz.prototype.compose = function compose (flow) {
  if (!flow && typeof flow !== 'string') {
    throw new TypeError('benz#compose expect string')
  }

  var self = this
  flow = flow === 'series' ? 'mapSeries' : flow
  flow = flow === 'parallel' ? 'map' : flow

  if (['mapSeries', 'map'].indexOf(flow) === -1) {
    throw new TypeError('benz#compose expect only `series` or `parallel` string')
  }

  return function factory (val, done) {
    var ctx = self || this
    if (typeof val === 'function') {
      val = [val]
    }
    control[flow](val, function iterator (fn, next) {
      next = self.options.settle ? function settleNext (err) {
        next(null, err)
      } : next

      letta(fn.bind(ctx), next).then(function (res) {
        next(null, res)
      }, next)
    }, done)
    return self
  }
}

Benz.prototype.series = function series (val, done) {
  return this.compose('series')(val, done)
}

Benz.prototype.settleSeries = function settleSeries (val, done) {
  this.options.settle = true
  return this.compose('series')(val, done)
}

Benz.prototype.parallel = function parallel (val, done) {
  return this.compose('parallel')(val, done)
}

Benz.prototype.settleParallel = function settleParallel (val, done) {
  this.options.settle = true
  return this.compose('parallel')(val, done)
}

test


var fs = require('fs')
var benz = Benz()

// benz.series(function|array|object, done)
// benz.parallel(function|array|object, done)
// benz.settleSeries(function|array|object, done)
// benz.settleParallel(function|array|object, done)

benz.settleSeries([
  function test1 () {
    return 123
  },
  function test2 () {
    throw new Error('foo')
  },
  function test3 (cb) {
    cb(null, 456)
  },
  function test4 (cb) {
    fs.readFile('packfdsf', cb)
  }
], function (err, res) {
  console.log('err', err)
  console.log('res', res)
})
tunnckoCore commented 8 years ago

hybrids redefined as of 4 feb, 2015.

tunnckoCore commented 8 years ago

async-control

/*!
 * async-control <https://github.com/hybridables/async-control>
 *
 * Copyright (c) 2016 Charlike Mike Reagent <@tunnckoCore> (http://www.tunnckocore.tk)
 * Released under the MIT license.
 */

'use strict'

var control = require('async')
var isObject = require('is-extendable')
var delegate = require('delegate-properties')

var methods = {
  compose: compose,
  series: compose('series'),
  parallel: compose('parallel'),
  iterator: iterator
}

module.exports = function asyncControl (app) {
  if (isObject(app)) {
    delegate(app, methods)
    return app
  }
  return module.exports
}

delegate(module.exports, methods)

function compose (flow, fn) {
  if (typeof flow !== 'string') {
    throw new TypeError('asyncControl.compose expect a string')
  }
  flow = flow === 'series' ? 'mapSeries' : flow
  flow = flow === 'parallel' ? 'map' : flow

  if (['mapSeries', 'map'].indexOf(flow) === -1) {
    throw new TypeError('async-control.compose: expect only strings: "series" or "parallel".')
  }
  return factory(flow, this)
}

function factory (flow, self) {
  return function _factory_ (val, done) {
    if (typeof val === 'function') {
      val = [val]
    }
    if (typeof val !== 'object' && typeof val !== 'function') {
      throw new TypeError('async-control methods expect array, object or function')
    }
    self = self || this
    self.iterator = self.iterator.call(self, self)

    if (typeof done !== 'function') {
      return function doneCallback (done) {
        control[flow](val, self.iterator, done)
      }
    }
    control[flow](val, self.iterator, done)
  }
}

function iterator (app) {
  app = app || this
  var opts = typeof app.options === 'object' ? app.options : {}
  var settle = typeof app.settle === 'boolean' ? app.settle : false
  opts.settle = typeof opts.settle === 'boolean' ? !!opts.settle : !!settle

  return function defaultIterator (fn, next) {
    function done (err, res) {
      if (err instanceof Error) {
        err.fn = fn
        return opts.settle ? next(null, err) : next(err)
      }
      next(null, res)
    }

    var res = null
    try {
      res = fn.call(app, done)
    } catch (err) {
      res = err
    }
    return res instanceof Error ? done(res) : (res ? done(null, res) : null)
  }
}

and merz

/*!
 * merz <https://github.com/hybridables/merz>
 *
 * Copyright (c) 2016 Charlike Mike Reagent <@tunnckoCore> (http://www.tunnckocore.tk)
 * Released under the MIT license.
 */

/* jshint asi:true */

'use strict'

var flow = require('./index')

flow.iterator = function iterator (app) {
  var opts = typeof app.options === 'object' ? app.options : {}
  var settle = typeof app.settle === 'boolean' ? app.settle : false
  opts.settle = typeof opts.settle === 'boolean' ? !!opts.settle : !!settle

  return function lettaIterator (fn, next) {
    require('letta').call(app, fn).then(function (res) {
      next.apply(app, [null].concat(res))
    }, function (err) {
      err.fn = fn
      if (opts.settle) {
        return next.call(app, null, err)
      }
      next.call(app, err)
    })
  }
}

module.exports = flow