seq/ChainWrapper.js

  1. import * as array from '../array'
  2. import * as collection from '../collection'
  3. import * as lang from '../lang'
  4. import * as math from '../math'
  5. import * as object from '../object'
  6. import concat from 'lodash/concat'
  7. import flow from 'lodash/flow'
  8. import mapValues from 'lodash/mapValues'
  9. import toPath from 'lodash/toPath'
  10. /**
  11. * Wrapper allowing to make sequences of immutadot functions calls on an object.<br/>
  12. * Instances are created by calling {@link seq.chain}.<br/>
  13. * The result of such sequences must be unwrapped with {@link seq.ChainWrapper#value}.
  14. * @memberof seq
  15. * @see {@link seq.chain} for more information.
  16. * @private
  17. * @since 0.1.8
  18. */
  19. class ChainWrapper {
  20. /**
  21. * This constructor should not be called directly.<br/>
  22. * Instances are created by calling {@link seq.chain}.
  23. * @param {Object} object The object to wrap.
  24. * @param {Array|string} [path] The path of the object on which functions are called.
  25. * @see {@link seq.chain} for more information.
  26. * @since 0.1.8
  27. */
  28. constructor(object, path) {
  29. this._object = object
  30. this._path = path
  31. this._flow = []
  32. }
  33. /**
  34. * Translates a relative path to an absolute path, using <code>this._path</code> as a base path.
  35. * @param {Array|string} path A relative path.
  36. * @return {Array} Absolute path.
  37. * @since 0.1.11
  38. */
  39. _absolutePath(path) {
  40. return concat(toPath(this._path), toPath(path))
  41. }
  42. /**
  43. * Add a function call to the sequence.
  44. * @param {function} fn The function to call.
  45. * @param {Array|string} path The path of the property to be set.
  46. * @param {...*} args The arguments for the function call.
  47. * @returns {seq.ChainWrapper} The wrapper instance.
  48. * @since 0.1.8
  49. */
  50. _call(fn, path, args) {
  51. this._flow.push(object => fn(object, this._absolutePath(path), ...args))
  52. return this
  53. }
  54. /**
  55. * Executes the chain sequence to resolve the unwrapped object.
  56. * @returns {Object} Returns the resolved unwrapped object.
  57. * @example
  58. * chain({ nested1: { prop: 'old' }, nested2: { prop: 1 } })
  59. * .set('nested1.prop', 'new')
  60. * .unset('nested2.prop')
  61. * .value() // => { nested1: { prop: 'new' }, nested2: {} }
  62. * @see {@link seq.chain|chain} for more information.
  63. * @since 0.1.8
  64. */
  65. value() {
  66. return flow(this._flow)(this._object)
  67. }
  68. }
  69. // Add namespaces functions to the ChainWrapper prototype
  70. [
  71. array,
  72. collection,
  73. lang,
  74. math,
  75. object,
  76. ].forEach(namespace => Object.assign(
  77. ChainWrapper.prototype,
  78. mapValues(
  79. namespace,
  80. fn => function(path, ...args) {
  81. return this._call(fn, path, args) // eslint-disable-line no-invalid-this
  82. }
  83. )
  84. ))
  85. export default ChainWrapper