Manual Reference Source Test

src/utilities/ClassUtility.js

import { stringToCamel, stringToSnake } from './FormatUtility';

/**
 * Support for mixin-style inheritance by extending from expressions yielding function objects.
 *
 * @param {Class}   baseClass - The base class
 * @param {Class[]} mixins    - The classes in which to mixin
 * @see http://es6-features.org/#ClassInheritanceFromExpressions
 *
 * @example
 * class Colored {
 *     initializer ()     { this._color = "white"; }
 *     get color ()       { return this._color; }
 *     set color (v)      { this._color = v; }
 * }
 *
 * class ZCoord {
 *     initializer ()     { this._z = 0; }
 *     get z ()           { return this._z; }
 *     set z (v)          { this._z = v; }
 * }
 *
 * class Shape {
 *     constructor (x, y) { this._x = x; this._y = y; }
 *     get x ()           { return this._x; }
 *     set x (v)          { this._x = v; }
 *     get y ()           { return this._y; }
 *     set y (v)          { this._y = v; }
 * }
 *
 * class Rectangle extends aggregation(Shape, Colored, ZCoord) {}
 *
 * var rect = new Rectangle(7, 42);
 * rect.z     = 1000;
 * rect.color = "red";
 * console.log(rect.x, rect.y, rect.z, rect.color);
 */
const aggregate = (baseClass, ...mixins) => {
  const base = class _Combined extends baseClass {
    constructor(...args) {
      super(...args);
      mixins.forEach((mixin) => {
        mixin.prototype.initializer.call(this);
      });
    }
  };
  const copyProps = (target, source) => {
    Object.getOwnPropertyNames(source)
      .concat(Object.getOwnPropertySymbols(source))
      .forEach((prop) => {
        if (prop.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/)) { return; }
        Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop));
      });
  };
  mixins.forEach((mixin) => {
    copyProps(base.prototype, mixin.prototype);
    copyProps(base, mixin);
  });
  return base;
};

/**
 * Updates field names from the current case to snake case
 *
 * @param  {Object} obj - Object in which to update field names
 * @return {Object}     - Object with fields in snake case
 */
function fieldsToCamel(obj) {
  if (obj === null || obj === undefined) {
    return obj;
  }

  Object.keys(obj).forEach((oldKey) => {
    const newKey = stringToCamel(oldKey);
    obj[newKey] = (typeof (obj[oldKey]) === 'object') ? fieldsToCamel(obj[oldKey]) : obj[oldKey];
    if (newKey !== oldKey) {
      delete obj[oldKey];
    }
  });
  return obj;
}

/**
 * Updates field names from the current case to snake case
 *
 * @param  {Object} obj - Object in which to update field names
 * @return {Object}     - Object with fields in snake case
 */
function fieldsToSnake(obj) {
  if (obj === null || obj === undefined) {
    return obj;
  }

  Object.keys(obj).forEach((oldKey) => {
    const newKey = stringToSnake(oldKey);
    obj[newKey] = (typeof (obj[oldKey]) === 'object') ? fieldsToSnake(obj[oldKey]) : obj[oldKey];
    if (newKey !== oldKey) {
      delete obj[oldKey];
    }
  });
  return obj;
}

function toJsonApiStub(obj) {
  let stub;
  if (obj) {
    if (Array.isArray(obj)) {
      stub = [];
      obj.forEach(o => stub.push(toJsonApiStub(o)));
    } else if (obj.id && obj.type) {
      stub = { id: obj.id, type: obj.type };
    } else {
      stub = null;
    }
  } else if (obj === null) {
    stub = null;
  }
  return stub;
}

export {
  aggregate,
  fieldsToCamel,
  fieldsToSnake,
  toJsonApiStub,
};