obj-chain-core
Version:
fluent chaining for obj with dot-prop access
425 lines (362 loc) • 11.8 kB
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>index.js - Documentation</title>
<script src="scripts/prettify/prettify.js"></script>
<script src="scripts/prettify/lang-css.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
</head>
<body>
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger" class="navicon-button x">
<div class="navicon"></div>
</label>
<label for="nav-trigger" class="overlay"></label>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ObjChain.html">ObjChain</a><ul class='methods'><li data-type='method'><a href="ObjChain.html#.init">init</a></li><li data-type='method'><a href="ObjChain.html#escape">escape</a></li><li data-type='method'><a href="ObjChain.html#extend">extend</a></li><li data-type='method'><a href="ObjChain.html#has">has</a></li><li data-type='method'><a href="ObjChain.html#setIfNotEmpty">setIfNotEmpty</a></li><li data-type='method'><a href="ObjChain.html#toString">toString</a></li><li data-type='method'><a href="ObjChain.html#use">use</a></li><li data-type='method'><a href="ObjChain.html#val">val</a></li></ul></li></ul><h3>Global</h3><ul><li><a href="global.html"></a></li><li><a href="global.html#configPlugin">configPlugin</a></li><li><a href="global.html#flow">flow</a></li><li><a href="global.html#remove">remove</a></li><li><a href="global.html#set">set</a></li><li><a href="global.html#write">write</a></li></ul>
</nav>
<div id="main">
<h1 class="page-title">index.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>const log = require('fliplog')
const Boss = require('likeaboss')
const ChainedMap = require('flipchain/ChainedMapExtendable')
const pkg = require('../package.json')
// @TODO:
// add `read-pkg-up` compatible with monorepo symlinks,
// because cannot store configstore values the same for every repo globally
// https://www.youtube.com/watch?v=SwSle66O5sU
const OFF = `${~315 >>> 3}@@`
let dynamics = {}
/**
* @param {Function[]} funcs functions to flow left to right
* @return {Function} passes args through the functions, bound to this
*/
function flow(...funcs) {
const length = funcs ? funcs.length : 0
return function flowing(...args) {
let index = 0
// log
// .data({length, index, f: funcs[index], args, funcs})
// .blue('flow')
// .echo()
// eslint-disable-next-line
let result = length ? funcs[index].apply(this, args) : args[0]
while (++index < length) {
// eslint-disable-next-line
result = funcs[index].call(this, result)
}
return result
}
}
/**
* @NOTE: only issue with this, is if the keys are the same...
* @TODO:
* - [ ] could auto define a `get` on every set, and a `set`...
* - [ ] make a lightweight, and dynamic version - with plugins
*
* changed to obj chain because it doesn't need to be json
* then we add middleware for observable, ~[immutable], json, file
*/
class ObjChain extends ChainedMap {
/**
* @param {string | Object} data json data
* @param {Array<string>} [plugins=[]]
* @return {ObjChain}
*/
static init(data, plugins) {
return new ObjChain(data, plugins)
}
/**
* @param {string | Object} data
* @param {Array<string>} [plugins=[]]
*/
constructor(data = {}, plugins = []) {
super()
// single arg just plugins
if (Array.isArray(data) === true) {
if (data[0] && data[0].includes('Plugin') === true) {
plugins = data
data = {}
}
}
this.pkg = pkg
this.data = data || {}
this.current = null
// @TODO: each of these, + pre & post
// @TODO: should use `sortByProp(['index'])`
this.middleware = {
setIfNotEmpty: [],
set: [],
get: [],
has: [],
del: [],
// val: [],
override: [],
init: [],
merge: [],
save: [],
}
// const aliased = {
// write: 'save',
// }
this.use = this.use.bind(this)
this.extend = this.extend.bind(this)
// plugins.push('MapPlugin')
this.store.set('debug', false)
plugins.forEach(plugin => {
log.magenta('using plugin').data(plugin).echo(this.store.get('debug'))
if (typeof plugin !== 'string') {
log
.magenta('using plugin - non-string')
.data(plugin)
.echo(this.store.get('debug'))
this.use(plugin)
}
else if (dynamics.exports[plugin]) {
log
.magenta('using plugin - dynamic')
.data(dynamics.exports[plugin])
.echo(this.store.get('debug'))
this.use(dynamics.exports[plugin])
}
else {
// log.quick({plugin, dynamics})
}
})
this.update = this.update.bind(this)
this.setIfNotEmpty = this.setIfNotEmpty.bind(this)
this.has = this.has.bind(this)
this.write = this.save.bind(this)
this.set = this.update.bind(this)
this.get = this.val.bind(this)
this.delete = this.del.bind(this)
this.remove = this.del.bind(this)
}
/**
* @alias set
* @param {string | any} key
* @param {Serializable | any} val
* @return {ObjChain} @chainable
*/
update(key, val) {
this.middleware.set.forEach(fn => fn(key, val))
return this
}
/**
* @param {string} key key with `.`
* @return {string} string with escaped `\\.`
*/
escape(key) {
return (key = key.replace('.', '\\.'))
}
/**
* @description sets !.has(key)
* @see ObjChain.has
* @param {string | any} key
* @param {Serializable | any} val
* @return {ObjChain} @chainable
*/
setIfNotEmpty(key, val) {
if (this.has(key)) return this
this.set(key, val)
return this
}
/**
* @param {string | any} key
* @return {Boolean}
*/
has(key) {
return this.middleware.has.map(fn => fn(key)).includes(true)
}
/**
* @alias delete
* @alias remove
* @description delete. remove
* @param {string | any} key
* @return {ObjChain}
*/
del(key) {
this.middleware.del.forEach(fn => fn(key))
return this
}
/**
* @description get a value
* @param {string | any | null} [key=null]
* @return {any} current key or named if there is key param
*/
val(key = null) {
// log.quick(this.middleware.val)
// .bind(this)
// return flow.apply(this, this.middleware.val)(key)
// return flow.apply(this, this.middleware.val)(key)
let val = key
this.middleware.get.forEach(fn => {
val = fn(key, val)
})
// log.quick(val, key)
return val
// const args = [key]
// const funcs = this.middleware.val
// const length = funcs ? funcs.length : 0
//
// let index = 0
// let result = length ? funcs[index].apply(this, args) : args[0]
// while (++index < length) {
// result = funcs[index].call(this, result)
// }
// log.quick(this)
}
/**
* @alias write
* @description save/write data
* @return {ObjChain}
*/
save() {
this.middleware.save.forEach(fn => fn(this))
return this
}
// --- plugins / middleware ---
/**
* @description use middleware
* @param {Object} obj
* @return {ObjChain}
*/
use(obj) {
if (typeof obj === 'function' && Object.keys(obj).length === 0) {
log.blue('use.fn').echo(this.store.get('debug'))
obj(this) // obj =
return this
}
if (obj.init) {
obj.init(this)
// delete obj.init
}
const keys = Object.keys(obj)
log.red('use.keys').data({keys}).echo(this.store.get('debug'))
for (let k = 0; k < keys.length; k++) {
const key = keys[k]
if (key === 'middleware') {
log.red('key is middlware').echo(this.store.get('debug'))
Object.keys(obj[key]).forEach(method => {
let fn = obj[key][method]
log
.data({method, key, k, fn, obj})
.green('using middleware')
.echo(this.store.get('debug'))
if (fn && fn.bind !== undefined) fn = fn.bind(this)
if (this.middleware[method] !== undefined) {
this.middleware[method].push(fn)
}
else {
log.data({method}).red('middleware for method not found 0.0')
}
})
// continue
}
else if (!obj[key] || obj[key].bind === undefined) {
log.yellow('use.else no bind').data({key}).echo(this.store.get('debug'))
this[key] = obj[key]
}
else {
log.yellow('use.else').data({key}).echo(this.store.get('debug'))
this[key] = obj[key].bind(this)
}
}
// log.quick(this.middleware)
return this
}
/**
* @TODO:
* - [ ] should validate here
* - [ ] .files('array')
* - [ ] allow nesting
* - [ ] (scripts().eh())
*
*
* @description take strings, make them methods to .set on
* @param {Array<string>} methods
* @param {boolean} [nest=false] nest the .set
* @return {ObjChain}
*/
extend(methods, nest = false) {
if (nest === true) {
methods.forEach(method => {
this[method] = (keyPath, data) => {
this.set(method + '.' + keyPath, data)
return this
}
})
return this
}
methods.forEach(method => {
const cb = data => {
this.set(method, data)
return this
}
// @TODO: should make each part a function instead of an obj...
if (method.includes('.') === true) this.set(method, cb)
else this[method] = cb
})
return this
}
/**
* @return {string}
*/
toString() {
return this.middleware.toString.forEach(fn => fn(this)).join('')
}
}
/**
* @param {string | Object} data json data
* @param {Array<string>} [plugins=[]]
* @return {ObjChain}
*/
// function ObjChainable(data, plugins = []) {
// // console.log({data}, Array.isArray(data), data[0])
// if (Array.isArray(data) === true) {
// if (data[0] && data[0].includes('Plugin') === true) {
// return new ObjChain({}, plugins)
// }
// }
// return new ObjChain(data, plugins)
// }
exports = module.exports = Boss.module(dynamics)
.dir(__dirname)
.main(ObjChain)
.props({pkg})
.dynamics([
{name: 'ConfigPlugin', path: './plugins/ConfigPlugin'},
{name: 'FilePlugin', path: './plugins/FilePlugin'},
{name: 'KebabPlugin', path: './plugins/KebabPlugin'},
{name: 'JSONPlugin', path: './plugins/JSONPlugin'},
{name: 'DotPropPlugin', path: './plugins/DotPropPlugin'},
{name: 'MapPlugin', path: './plugins/MapPlugin'},
{name: 'SnapshotPlugin', path: './plugins/SnapshotPlugin'},
{name: 'DiffPlugin', path: './plugins/DiffPlugin'},
// {name: 'FlowPlugin', path: './plugins/FlowPlugin'},
// {name: 'ObservablePlugin', path: './plugins/ObservablePlugin'},
// {name: 'DebugPlugin', path: './plugins/DebugPlugin'},
// {name: 'GetSetPlugin', path: './plugins/GetSetPlugin'},
// {name: 'ProxyPlugin', path: './plugins/ProxyPlugin'},
// {name: 'ZipPlugin', path: './plugins/ZipPlugin'},
])
.end()
</code></pre>
</article>
</section>
</div>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.3</a> on Fri Apr 28 2017 01:03:22 GMT-0700 (PDT) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
</footer>
<script>prettyPrint();</script>
<script src="scripts/linenumber.js"></script>
</body>
</html>