Novel Preposterous Mockery

    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')

    Install

    npm i flipchain

    DownloadsWeekly Downloads

    60

    Version

    0.6.0

    License

    MIT

    Last publish

    Collaborators

    • aretecode