flipchain
TypeScript icon, indicating that this package has built-in type declarations

0.6.0 • Public • Published

⛓ flipchain

NPM version MIT License fliphub flipfam

core chaining library, heavily based on 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

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

Chain.prop()
Chain.longer()
Chain.intoSomeShapes()

or throwing a really huge rock into the water, and getting the splashes to make more splashes.

Chain.from({
  prop: null,
  longer: null,
  intoSomeShapes: null,
})

pebbles using method chaining looks similar to skipping rocks

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

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

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

🌐 api

  • every chain has .className for easy debugging
  • every chain has this.parent
  • and this.parent is hidden on inspection by 🕵🗜 inspector-gadget for easier debugging

ChainedSet

  • 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

extendAlias

  • (methodsToAlias: Array<string>, 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)
  • 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

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

  // using `izz` to validate types when setting
  this.extendType(['str'], 'string')

Package Sidebar

Install

npm i flipchain

Weekly Downloads

1

Version

0.6.0

License

MIT

Last publish

Collaborators

  • aretecode