Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/fliphub/flipchain
NOTICE: replaced by https://github.com/fluents/chain-able
https://github.com/fliphub/flipchain
chain chainable fluent map set
Last synced: 27 days ago
JSON representation
NOTICE: replaced by https://github.com/fluents/chain-able
- Host: GitHub
- URL: https://github.com/fliphub/flipchain
- Owner: fliphub
- Created: 2017-05-09T02:50:34.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2017-05-14T08:26:08.000Z (over 7 years ago)
- Last Synced: 2024-08-14T07:03:11.033Z (5 months ago)
- Topics: chain, chainable, fluent, map, set
- Homepage:
- Size: 15.6 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# ⛓ flipchain
[![NPM version][flipchain-npm-image]][flipchain-npm-url]
[![MIT License][license-image]][license-url]
[![fliphub][gitter-badge]][gitter-url]
[![flipfam][flipfam-image]][flipfam-url]
[![fluent](https://img.shields.io/badge/⛓-fluent-9659F7.svg)](https://www.npmjs.com/package/flipchain)[flipchain-npm-image]: https://img.shields.io/npm/v/flipchain.svg
[flipchain-npm-url]: https://npmjs.org/package/flipchain
[license-image]: http://img.shields.io/badge/license-MIT-blue.svg?style=flat
[license-url]: https://spdx.org/licenses/MIT
[gitter-badge]: https://img.shields.io/gitter/room/fliphub/pink.svg
[gitter-url]: https://gitter.im/fliphub/Lobby
[flipfam-image]: https://img.shields.io/badge/%F0%9F%8F%97%20%F0%9F%92%A0-flipfam-9659F7.svg
[flipfam-url]: https://www.npmjs.com/package/flipfam> core chaining library, heavily based on [webpack-chain](https://github.com/mozilla-rpweb/webpack-chain), but not webpack-specific.
# 🏰 benefits
writing an api using flipchain means writing a single fluent api, but getting 3 apis as a result!
- 🍉 rehydratable configurations
- ⛓ fluent chainable api
- 🍦 object configs that are easily merged deep# 🌊 what?
![fluent](https://upload.wikimedia.org/wikipedia/commons/thumb/e/ef/Stone_skimming_-Patagonia-9Mar2010.jpg/1920px-Stone_skimming_-Patagonia-9Mar2010.jpg)imagine there are 2 people by the water, & the goal is to make 10 splashes.
as with most skills, you get better at skipping rocks the more that you do it.
a lot of people cannot skip rocks, or they do not like to skip rocks, but they can still throw rocks and make a splash. think of this like non-fluent/vanilla-calls
```js
Chain.prop()
Chain.longer()
Chain.intoSomeShapes()
```or throwing a really huge rock into the water, and getting the splashes to make more splashes.
```js
Chain.from({
prop: null,
longer: null,
intoSomeShapes: null,
})
```![pebbles](http://www.joulesevans.com/wp-content/uploads/2012/01/pebbles-mentor-page.jpg)
using method chaining looks similar to skipping rocks
```js
Chain
.prop()
.longer()
.intoSomeShapes()
```writing an application with a fluent interface allows people to use it all three ways, and you only have to write it one way.
# 📘 examples
## 👋 intro
```js
const ChainedMap = require('./ChainedMapExtendable')class EasyFluent extends ChainedMap {
constructor(parent) {
super(parent)// extend a list of strings for easy chainable methods
this.extend(['eh'])// same as .extend,
// but when called with no arguments,
// default is used (`true` in this case)
// third param is optionally a prefix for inversified
// for example, `no` => `noCanada()` for inverse value
this.extendPrefixed(['canada'], true, 'no')
}// if more advanced data changes are needed
// or if the syntax is preferred for use with typescript or flowtype
// .set, .get, .has are available
igloo(igloo) {
this.set('igloo', igloo)
return this
}toConfig() {
return this.entries()
}
}// {igloo: 'fire', canada: false, eh: 'moose'}
const config = new EasyFluent()
.igloo('fire')
.noCanada()
.eh('moose')
.toConfig()// this is == config
const hydrated = new EasyFluent()
.from(config)
.toConfig()// canada is now true
const merged = new EasyFluent()
.merge(config)
.merge({canada: true})
.toConfig()
```## 🕳🏊 advanced
```js
const ChainedMap = require('./ChainedMapExtendable')
const ChainedSet = require('./ChainedSet')class Advanced extends ChainedMap {
static init(parent) {
return new Advanced(parent)
}
constructor(parent) {
super(parent)
this.list = new ChainedSet(this)
this.extend(['eh'])
this.extendWith(['canada'], true)
}addName(name) {
this.list.add(name)
return this
}igloo(igloo) {
this.set('igloo', igloo)
return this
}toConfig() {
return Object.assign(this.entries(), {
list: this.list.values().map(name => name),
})
}// since we have additional data that is not simple key value
// we do additional (albeit easy) steps to rehydrate
from(obj) {
super.from(obj)Object
.keys(obj)
.forEach(key => {
const val = obj[key]
switch (key) {
case 'list': return val
.filter(name => name)
.forEach(name => this.addName(name))
}
})return this
}// same with `from`
// we do additional simple steps to merge in lists
merge(obj) {
Object
.keys(obj)
.filter(key => obj[key])
.forEach(key => {
const val = obj[key]
switch (key) {
case 'list': return val
.filter(name => name)
.forEach(v => this.addName(v))
}
})// built-in merging
// can use `.mergeReal` to merge only `real` values
// and `.merge` to merge any
super.merge(obj)return this
}
}const chain = Advanced
.init()
.igloo('brr')
.canada()
.eh('eh!')
.addName('thing one')
.addName('thing two')// true, `eh!`
chain.has('igloo')
chain.get('eh')const result = chain.toConfig()
const hydrated = Advanced
.init()
.from(result)
.toConfig()const merged = Advanced
.init()
.merge(hydrated)
.merge({igloo: 'whaaaat'})// can use toConfig,
// and safely continue editing `merged`
// with a snapshot of the object data saved as `mergedResult`
const mergedResult = merged.toConfig()// hydrated === result === {
// igloo: 'brr',
// canada: 'canada',
// eh: 'eh!',
// list: [ 'thing one', 'thing two' ]
// }// merged === {
// igloo: 'whaaaat',
// canada: 'canada',
// eh: 'eh!',
// list: [ 'thing one', 'thing two' ]
// }
```# 🌊 types
- there are [jsdoc blocks](http://usejsdoc.org/index.html) for all methods# 🌐 api
- every chain has `.className` for easy debugging
- every chain has this.parent
- and this.parent is hidden on inspection by [🕵🗜 inspector-gadget](https://www.npmjs.com/package/inspector-gadget) for easier debugging### ChainedSet
- [Set](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Set)
- prepend => this
- clear => this
- delete => this
- values => array of entries
- has => boolean
- merge => merge an object into the chain
- when => conditional instance callback### ChainedMap
- [Map](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map)#### extendAlias
- `(methodsToAlias: Array, methodToAlias: string, [thisArg])`
- alias a list of methods
- @returns `this`### from
- `(obj: Object)`
- checks each property of the object
- calls the chains accordingly
- rehydrates a chain from an object### other
- decorateParent (using [childparent](https://www.npmjs.com/package/childparent))
- clear() => this // clearsAll
- delete(key) => this
- entries => {keysAndValues}
- values => Object.values
- get(key) => entry
- has(key) => boolean
- set(key, val) => this##### if key is an array, merge in the value,
##### usually should use ChainedSet for this
- concat(key, val) => this
- append(key, val) => this##### merging
- mergeReal(obj) => this // only merges non-undefined values
- merge(obj) => this
- clean => this
- when => conditional instance callback# 🔗 links & more
- [Martin Fowler on FluentInterface](https://www.martinfowler.com/bliki/FluentInterface.html)# `chainMapTill`
lets you chain until the required keys are set via chains, or if they are passed in, then it auto returns `parent`# `chainedMapExtendable`
- has chains with `.extends` able to use `default` values when calling it
- also can add `prefixes` (default `no`) so if you use `cache` default `true`, it can add `noCache` which does the inverse
- set up for being chains of chains when you add a few decorating chains dynamically# 📝🌊 TODO
```js
// using `izz` to validate types when setting
this.extendType(['str'], 'string')
```