performer/apply.js

const cloneDeep = require('lodash.clonedeep')

const utils = require('./utils/utils.js')
const isObjNotNull = utils.isObjNotNull

/**
 * Apply values/getters on COM
 *
 * @private
 * @class
 */
function Apply() {

}


module.exports = new Apply()


/**
 * Apply getters values
 */
Apply.prototype.getters = function applyGetters(schema, node) {
  Object.keys(schema._cvtProperties).forEach((name) => {
    const mySchema = schema._cvtProperties[name]
    if (mySchema._cvtProperties) {
      if (!node[name]) {
        node[name] = {}
      }
      applyGetters.call(this, mySchema, node[name])
    } else {
      const currentOrigin = mySchema.getOrigin()
      const currentLevel = (currentOrigin) ? this._getters.order.indexOf(currentOrigin) : 0

      for (let i = this._getters.order.length - 1; i >= 0; i--) {
        if (i < currentLevel) {
          break // stop if the current getter is higher
        }

        const getterName = this._getters.order[i] // getterName
        const getterObj = this._getters.list[getterName]
        let propagationAsked = false // #224 accept undefined

        if (!getterObj || !(getterName in mySchema.attributes)) {
          continue
        }
        const getter = getterObj.getter
        const value = cloneDeep(mySchema.attributes[getterName])
        const stopPropagation = () => { propagationAsked = true }

        /**
         * Will get an external value depending of custom getter/code
         *
         * @callback ConfigObjectModel.getterCallback
         *
         * @example
         * blueconfig.addGetter({
         *   property: 'accept-undefined',
         *   getter: (value, schema, stopPropagation) => schema._cvtCoerce(fs.readFileSync(value, 'utf-8').toString()),
         *   usedOnlyOnce: true // use file only once
         * });
         *
         * @param    {*}           value       Value to coerce
         * @param    {string}      mySchema    Value to coerce
         * @param    {function}    value       Stop propagation (accept undefined value). By default,
         *                                     undefined don't stop the getter queue, this mean Blueconfig
         *                                     will continue to call other getter to find a value not undefined.
         *
         * @returns  {*}    value    Returns coerced value
         */
        // call getter                        vvvvvvvvvvvvvvvvvvv Support prevent breakchanges
        node[name] = getter.call(this, value, mySchema.attributes, stopPropagation)

        if (typeof node[name] !== 'undefined' || propagationAsked) {
          // We use function because function are not saved/exported in schema
          mySchema._private.origin = getterName
          break
        }
      }
    }
  })
}


/**
 * Apply values from config merged
 */
Apply.prototype.values = function applyValues(from, to, schema) {
  const indexVal = this._getters.order.indexOf('value')
  Object.keys(from).forEach((name) => {
    const mySchema = (schema && schema._cvtProperties) ? schema._cvtProperties[name] : null
    // leaf
    if (Array.isArray(from[name]) || !isObjNotNull(from[name]) || !schema || schema.format === 'object') {
      const lastG = mySchema && mySchema.getOrigin && mySchema.getOrigin()

      if (lastG && indexVal < this._getters.order.indexOf(lastG)) {
        return
      }

      to[name] = (mySchema && mySchema.coerce) ? mySchema.coerce(from[name]) : from[name]
      if (lastG) {
        mySchema._private.origin = 'value'
      }
    } else {
      if (!isObjNotNull(to[name])) to[name] = {}
      applyValues.call(this, from[name], to[name], mySchema)
    }
  })
}