Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
@skava/modules / ___dist / composition / closureComposition.js
Size: Mime:
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
var _exportNames = {
  composition: true,
  coerceResponseWithSchema: true,
  autofixTypes: true,
  coerceAndAutofixResponseWithSchema: true
};
exports.composition = composition;
exports.coerceResponseWithSchema = coerceResponseWithSchema;
exports.autofixTypes = autofixTypes;
exports.default = exports.coerceAndAutofixResponseWithSchema = void 0;

var _chainAbleBoost = require("chain-able-boost");

var _exotic = require("exotic");

var _ramda = require("ramda");

var _lodash = require("lodash");

var _deps = require("../deps");

var _get = require("./get");

Object.keys(_get).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function get() {
      return _get[key];
    }
  });
});
// @NOTE named - by ref
// exported.cast = {cast, is}
// make Ramda's names better
// R.allButLast = R.init
// R.removePathInObj = R.dissocPath
// @TODO use `Nil` type
// just adapt this to use types in composition and lotsa fun
// const {
//   isObj,
//   isArray,
//   isNumber,
//   isBoolean,
//   isString,
//   toBoolean,
//   toString,
//   toNumber,
//   toArray,
//   toObject,
//   isFunction,
// } = Typed.spread(Object, Array, Function, Number, String, Boolean)
const primitives = [(0, _exotic.Typed)(Boolean), (0, _exotic.Typed)(Number), (0, _exotic.Typed)(String)];

const find = (value, path) => {
  return primitives.find(Type => Type.is(value, path)) || {
    coerce: _chainAbleBoost.identity
  };
};
/**
 * @NOTE this is the only piece being used in this
 * @NOTE there is an opinionated decision here that can be improved if required
 *       where ONLY schema is traversed, and *applied* to the data
 *       and it doesn't double traverse
 *       (mainly because it allows defaulting)
 *
 * 1. recurse [schema/x] (call self)
 * 2. .get from response
 * 3. use types in `x` -> schema -> nested types -> coerce response
 * 4. how to handle response data best that is not in there, could just `pluck` style, or throw
 *
 * @alias evolveResponseWithSchema
 * @param {Object} schema types
 * @param {*} response data
 * @return {*} based on response + schema coersion
 */


function coerceResponseWithSchema(x, response) {
  if ((0, _exotic.isNil)(x)) {
    return response;
  }

  if ((0, _exotic.isString)(x)) {
    return (0, _exotic.toString)(response);
  }

  if ((0, _exotic.isNumber)(x)) {
    return (0, _exotic.toNumber)(response);
  }

  if ((0, _exotic.isBoolean)(x)) {
    return (0, _exotic.toBoolean)(response);
  }

  if ((0, _exotic.isObj)(x)) {
    let coerced = (0, _exotic.toEmpty)(x);
    (0, _chainAbleBoost.forOwn)(x, (value, key) => {
      // @NOTE could use R.prop whatever
      coerced[key] = coerceResponseWithSchema(value, (0, _lodash.get)(response, key));
    });

    if ((0, _exotic.isArray)(x)) {
      return (0, _exotic.toArray)(coerced);
    } else {
      return (0, _exotic.toObject)(coerced);
    }
  }

  console.log(x);
  throw new TypeError('missing some data!');
}
/**
 * @NOTE this is the same as coercePrimitives, but does not `flatten`
 * @TODO @JAMES eventually this gets integrated with json parsing in api call middleware @PERF
 * @TODO split this elsewhere just temp autofixing coersion in defaulting data
 * @see coercePrimitives
 * @param {*} response
 * @return {*} response with fixed data, or better-than-null
 */


function autofixTypes(response) {
  if ((0, _exotic.isNil)(response)) {
    return _exotic.EMPTY_STRING;
  }

  if ((0, _exotic.isString)(response)) {
    return (0, _exotic.toString)(response);
  }

  if ((0, _exotic.isNumber)(response)) {
    return (0, _exotic.toNumber)(response);
  }

  if ((0, _exotic.isBoolean)(response)) {
    return (0, _exotic.toBoolean)(response);
  }

  if ((0, _exotic.isObj)(response)) {
    let coerced = (0, _exotic.toEmpty)(response);
    (0, _chainAbleBoost.forOwn)(response, (value, key) => {
      // @NOTE could use R.prop whatever
      coerced[key] = autofixTypes(value, (0, _lodash.get)(response, key));
    });

    if ((0, _exotic.isArray)(response)) {
      return (0, _exotic.toArray)(coerced);
    } else {
      return (0, _exotic.toObject)(coerced);
    }
  }

  throw new TypeError('missing some data! ' + JSON.stringify(response));
}

const coerceAndAutofixResponseWithSchema = (schema, response) => coerceResponseWithSchema(schema, autofixTypes(response));
/**
 * @closure
 * @return {*} whatever (object)
 * @param {*} original data
 *
 * could also use all schemas or whatever here,
 *
 * could say split back out as 1 line fns
 * and use as constructor in here with curries
 *
 * much stuff to do
 */


exports.coerceAndAutofixResponseWithSchema = coerceAndAutofixResponseWithSchema;

function composition(original) {
  let target = (0, _ramda.clone)(original);
  /**
   * @desc closure javascript fp true style epicness
   * @constructor
   */

  function constructor() {
    // waaaaat
    const fp = Object.create(null);
    fp.liftRecursive = liftRecursive;
    fp.target = target;
    fp.reshapeWhere = reshapeWhere;
    fp.transformWhere = transformWhere;
    fp.coercePrimitives = coercePrimitives;
    fp.liftProp = liftProp;
    fp.liftPaths = liftPaths;
    fp.coerceResponseWithSchema = coerceResponseWithSchema;
    fp.autofixTypes = autofixTypes;
    return fp;
  }

  const fp = constructor();
  /**
   * @TODO property, value
   * @TODO can use http://ramdajs.com/docs/#applySpec - for some of the lifting
   * @see http://funkyjavascript.com/lenses/
   * @curried 2
   *
   * @alias evolveWhere
   * @param {Object} schema to match & reschema
   * @param {Object} obj object to transform
   * @return {Object} transformed object
   */

  function transformWhere(schema, obj) {
    let transformed = obj; // recurse the schema

    (0, _chainAbleBoost.recurse)(obj).forEach((x, key, traverser) => {
      if (traverser.isRoot) {
        return;
      } // @TODO !!!!!!!! THIS PART I LOST IT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! (and api changed)


      const evolution = schemaEvolver(x, key); // when we have an evolution, use it

      if (evolution !== false) {
        const setForPath = (0, _ramda.lens)((0, _ramda.path)(traverser.path), (0, _ramda.assocPath)(traverser.path)); // apply the evolution to this path setting immutably

        transformed = (0, _ramda.set)(setForPath, evolution, transformed);
      }
    });
    return transformed;
  }
  /**
   * @param {Array<string> | string} property to lift deeply into top-level
   * @TODO @MICHAEL requires logic for duplication on multi-levels
   */


  function liftRecursive(property) {
    let lifted = (0, _exotic.toEmpty)(target);
    (0, _chainAbleBoost.recurse)(target).forEach((x, key, traverser) => {
      // only exact property matches
      if (key !== property) {
        return;
      } // dot-prop-path


      const path = traverser.path.slice();
      const pathUp = (0, _ramda.init)(path); // no parent

      if (pathUp.length === 0) {
        return;
      } // STUPID THING


      const setForPath = R.lens(R.path(pathUp), R.assocPath(pathUp));
      const sliced = R.set(setForPath, x, lifted);
      lifted = (0, _chainAbleBoost.merge)(lifted, sliced); // @NOTE shallow copy means 1 level deep sheesh
      // lifted = Object.assign({}, lifted, sliced)

      lifted = (0, _ramda.dissocPath)(path, lifted);
    }); // immutable with `lifted` can merge(target, lifted, {clone: true})
    // for however you want to have a history of transformations :-)
  }
  /**
   * @desc changes all primitives that are not their correct type, to the correct version
   * @param {Object} data
   * @return {Object} coerced data
   * @example "false" -> false
   */


  function coercePrimitives(data) {
    const flat = (0, _chainAbleBoost.toUniverseView)(data, true);
    (0, _chainAbleBoost.forOwn)(flat, (value, path) => {
      // which primitive wants this one
      const Type = find(value, path); // upgrade it

      flat[path] = Type.coerce(value, path);
    });
    return flat;
  }
  /**
   * @desc traverses the OBJECT (response) to apply DEFAULTS based on TYPE or AUTOFIX data
   *
   * @alias defaulto
   * @param {Object} schema
   * @param {*} obj
   * @return {*}
   */


  function coerceOrDefault(schema, obj) {
    let transformed = obj;
    (0, _chainAbleBoost.recurse)(obj).forEach((x, key, traverser) => {
      // ignore first
      if (!key) {
        return;
      }

      const path = traverser.path;
      let evolution = (0, _lodash.get)(schema, path); // when we have an evolution, use it

      if (evolution !== undefined) {
        // get type
        const EvolutionType = _exotic.Typed.findAll(evolution)[0];

        if ((0, _exotic.isNative)(evolution)) {
          // if no default value, coerce to default
          evolution = EvolutionType.coerce;
        } else {
          // with a default,
          //    check type,
          // if not that type,
          //    default it (would've autocoerced before this step)
          const defaulValue = evolution;

          evolution = x => EvolutionType.is(x) ? x : defaulValue;
        }

        (0, _lodash.set)(transformed, path, evolution(x));
      }
    });
    return transformed;
  }
  /**
   * @param  {Object} data data to reshape
   * @param  {string} property single property to lift up
   * @return {Object} lifted
   */


  function liftProp(data, property) {
    const value = (0, _lodash.get)(data, property);

    if (!value) {
      return data;
    }

    let merged = (0, _chainAbleBoost.merge)(data, value, {
      clone: true
    });
    let lifted = (0, _deps.withoutProp)(property, merged);
    return lifted;
  }
  /**
   * @param  {Object} data data to reshape
   * @param  {Array<string>} paths list of paths to lift up
   * @return {Object} lifted
   */


  function liftPaths(data, paths) {
    let lifted = data;
    (0, _chainAbleBoost.forOwn)(paths, path => {
      lifted = liftProp(data, path);
    });
    return lifted;
  }
  /**
   * @see http://funkyjavascript.com/lenses/
   * @tutorial  https://github.com/ramda/ramda-lens/tree/master/src
   * @param  {Object} data data to reshape
   * @param  {Object} shape shape to reshape when it fits
   * @param  {Function} evolution make it fit - playdoe
   * @return {Object} reshaped
   */


  function reshapeWhere(data, shape, evolution) {
    let reshaped = data;

    const matchShape = (x, key) => {
      if ((0, _exotic.isFunction)(shape)) {
        return shape(x, key);
      }

      const doesMatch = (0, _chainAbleBoost.where)(shape, x, key);
      return doesMatch;
    };

    (0, _chainAbleBoost.recurse)(this.dump).forEach((x, key, traverser) => {
      const isFound = matchShape(x, key);

      if (!isFound) {
        return;
      }

      const setForPath = (0, _ramda.lens)((0, _ramda.path)(traverser.path), (0, _ramda.assocPath)(traverser.path));
      const setted = evolution(x);
      reshaped = (0, _ramda.set)(setForPath, setted, reshaped);
    });
    return reshaped;
  }

  return fp;
}

var _default = composition;
exports.default = _default;