lyfeyaj/poplar

View on GitHub
lib/dynamic.js

Summary

Maintainability
A
55 mins
Test Coverage
/**
 * Expose `Dynamic`.
 */
module.exports = Dynamic;

/*!
 * Module dependencies.
 */
var debug = require('debug')('poplar:dynamic');
var _ = require('lodash');
var assert = require('assert');

/**
 * @class
 * Create a dynamic value from the given value.
 *
 * @param {*} val The value object
 * @param {Context} ctx The Context
 */
function Dynamic(val, ctx) {
  this.val = val;
  this.ctx = ctx;
}

/*!
 * Object containing converter functions.
 */
Dynamic.converters = {};

/**
 * Define a named type conversion. The conversion is used when a
 * `ApiMethod` argument defines a type with the given `name`.
 *
 * ```js
 * Dynamic.define('MyType', function(val, ctx) {
 *   // use the val and ctx objects to return the concrete value
 *   return new MyType(val);
 * });
 * ```
 *
 * @param {String} name The type name
 * @param {Function} converter
 */
Dynamic.define = function(name, converter) {
  this.converters[name] = converter;
};

/**
 * undefine a converter via its name
 */
Dynamic.undefine = function(name) {
  delete this.converters[name];
};

/**
 * Is the given type supported.
 *
 * @param {String} type
 * @returns {Boolean}
 */
Dynamic.canConvert = function(type) {
  return !!this.getConverter(type);
};

/**
 * Get converter by type name.
 *
 * @param {String} type
 * @returns {Function}
 */
Dynamic.getConverter = function(type) {
  return this.converters[type];
};

/**
 * Shortcut method for convert value
 *
 * @param {String} val
 * @param {String} type
 * @param {Object} ctx
 * @returns {Object}
 */
Dynamic.convert = function(val, toType, ctx) {
  if (Array.isArray(toType)) {
    if (!Array.isArray(val)) {
      if (val === undefined || val === '') {
        val = [];
      } else {
        val = [val];
      }
    }

    return Dynamic.convert(val, toType[0], ctx);
  }

  if (Array.isArray(val)) {
    return _.map(val, function(v) {
      return Dynamic.convert(v, toType, ctx);
    });
  }
  return (new Dynamic(val, ctx)).to(toType);
};

/**
 * Convert the dynamic value to the given type.
 *
 * @param {String} type
 * @returns {*} The concrete value
 */
Dynamic.prototype.to = function(type) {
  var converter = this.constructor.getConverter(type);
  assert(converter, 'No Type converter defined for ' + type);
  return converter(this.val, this.ctx);
};

/**
 * Built in type converters...
 *   number
 *   date
 *   string
 *   boolean
 *   any
 */

Dynamic.define('number', function convertNumber(val) {
  if (val === 0) return val;
  if (!val) return val;
  return Number(val);
});

Dynamic.define('date', function convertDate(val) {
  if (val instanceof Date) return val;
  if (!val) return val;
  return new Date(val);
});

Dynamic.define('string', function convertString(val) {
  if (typeof val === 'string') return val;
  if (!val) return val;
  return String(val);
});

Dynamic.define('boolean', function convertBoolean(val) {
  switch (typeof val) {
    case 'string':
      switch (val) {
        case 'false':
        case 'undefined':
        case 'null':
        case '0':
        case '':
          return false;
        default:
          return true;
      }
      break;
    case 'number':
      return val !== 0;
    default:
      return Boolean(val);
  }
});

Dynamic.define('any', function convertAny(val) {
  return val;
});