0.0.1-security • Published 2 years ago

@tsers/model v0.0.1-security

Weekly downloads
-
License
-
Repository
-
Last release
2 years ago

TSERSful Model Interpreter

Manage your application state in a TSERSful way.

Travis Build Code Coverage NPM version Gitter GitHub issues

Usage

Installation

npm i --save @tsers/model

Using the interpreter

@tsers/model provides a factory function which can be used to construct the actual interpreter. That factory function takes one mandatory parameter: initialState The initial state can be anything. Usually it is a JSON type like object, array or primitive.

import TSERS from "@tsers/core"
import Model from "@tsers/model"
import main from "./YourApp"

TSERS(main, {
  model$: Model({counter: 0, list: ["foo", "bar"]})
})

API reference

Signals

Model interpreter instance is itself an observable that emits values every time when the application state changes. Because the model inherits an observable, you can use it like you'd use any other observable in your app:

TSERS(main, {
  DOM: ReactDOM("#app"),
  model$: Model(0)
})

function main({DOM, model$}) {
  const {h} = DOM
  const vdom$ = DOM.prepare(model$.map(counter =>
    h("h1", `Counter value is ${counter}`)))
  // ...
}

Model interpreter provides also the following signal transform functions

lens :: (PartialLens, ...PartialLens) => SubModel

Slices a sub-model from the original model that is bi-directionally connected to the original model (when the original model changes, then also the lensed sub-model changes and vice versa). Accepts one or many partial.lenses compatible lenses as arguments. For more information about lenses, please see partial.lenses docs.

TSERS(main, {
  DOM: ReactDOM("#app"),
  model$: Model({a: 0, b: 10})
})

function main({DOM, model$}) {
  const {h} = DOM
  const counterA$ = model$.lens("a")
  const counterB$ = model$.lens("b")
  // ...
}

mod :: Observable (currentState => newState) => Observable Mod

Takes an observable of modify functions (currentState => newState) and converts them into the form that Model interpreter understands. This transform must be applied to the output signals (see Output signals section) from application to the model interpreter.

TSERS(main, {
  model$: Model(0)
})

function main({model$, mux}) {
  const incClick$ = ...
  
  const inc$ = incClick$.map(() => state => state + 1)
  return mux({
    model$: model$.mod(inc$)
  })
}

set :: Observable newState => Observable Mod

Just a convenience transform function to .mod(Observable(() => newState))

TSERS(main, {
  model$: Model("tsers")
})

function main({model$, mux}) {
  const textChange$ = ...
  
  const text$ = textChange$.map(e => e.target.value)
  return mux({
    model$: model$.set(text$)
  })
}

mapListById :: ((id, Model item) => Observable A) => Observable (Observable A)

If the model contains list of items, this helper transforms maps the list item with the given iterator function so that the iterator function receives two arguments: id of the mapped list item and the lensed sub-model of the item. Iterator function should return an observable.

This method behaves exactly like mapListById transform from @tsers/core but the second parameter is a lensed sub-model instead of an observable.

TSERS(main, {
  model$: Model([{id: 1, value: 0}, {id: 2, value: 0}])
})

function main(signals) {
  const {model$} = signals
  const children$$ = model$.mapListById((id, counter$) =>
      Counter({...signals, model$: counter$.lens("value")}))
  // ...
}

mapListBy :: ((item => ident), (ident, Model item) => Observable A) => Observable (Observable A)

Same as mapListById but allows to define the identity function instead of using .id property

TSERS(main, {
  model$: Model([{_id: "123abc", value: 0}, {_id: "341afd", value: 0}])
})

function main(signals) {
  const {model$} = signals
  const children$$ = model$.mapListBy(item => item._id, (_id, counter$) =>
      Counter({...signals, model$: counter$.lens("value")}))
  // ...
}

log :: String => Model

Allows logging the (sub-)model changes with the given prefix.

TSERS(main, {
  model$: Model(0)
})

function main({model$}) {
  // model$ is now exactly same model as before, but every time when the value
  // changes, the changed state is logged to the JS console
  model$ = model$.log("Counter:")
  // ...
}

Output signals

Model interpreter expect a stream of state modifications (modify functions prepared by Model.mod(..) or new state values prepared by Model.set(...)) and changes the model's state based on those modifications.

TSERS(main, {
  model$: Model(0)
})

function main({model$, mux}) {
  const incClick$ = ...
  const resetClick$ = ...
  
  const incMod$ = model$.mod(incClick$.map(() => state => state + 1))
  const resetMod$ = model$.set(resetClick$.map(() => 0))
  return mux({
    model$: O.merge(incMod$, resetMod$)
  })
}

License

MIT