cibernox/ember-cpm

View on GitHub
addon/utils.js

Summary

Maintainability
A
1 hr
Test Coverage
/**
  EmberCPM Utils

  @module utils
  @requires ember
*/

import { typeOf } from '@ember/utils';
import { A } from '@ember/array';
import computed from 'ember-macro-helpers/computed';
import computedUnsafe from 'ember-macro-helpers/computed-unsafe';

/**
 Builds a computed property macro

 @method resolveKeysUnsafe
 @for utils
 @param {Function} callback passed resolved values to calculate the property
 */
export function resolveKeys(callback) {
  return function() {
    return computed(...arguments, callback);
  };
}

/**
 Builds a computed property macro (unsafe version)

 @method resolveKeysUnsafe
 @for utils
 @param {Function} callback passed resolved values to calculate the property
 */
export function resolveKeysUnsafe(callback) {
  return function() {
    return computedUnsafe(...arguments, callback);
  };
}

/**
 Builds a computed property macro based on array reducing

 @method reduceKeysUnsafe
 @for utils
 @param {Function} callback passed resolved values to calculate the property
 */
export function reduceKeysUnsafe(callback) {
  return resolveKeysUnsafe((...values) => {
    values = values.reduce((values, valueOrArray) => {
      return values.concat(valueOrArray);
    }, []);
    if (values.length === 0) {
      return 0;
    }
    return values.reduce(callback);
  });
}

/**
 Generate a "parse-like" computed property macro

 Example:
 ```js
 parseComputedPropertyMacro(function (raw) {return parseFloat(raw);});
 ```

 @method parseComputedPropertyMacro
 @param {function} parseFunction single-argument function that transforms a raw value into a "parsed" value
 */
export function parseComputedPropertyMacro (parseFunction) {
  return function(dependantKey) {
    if ('undefined' === typeof dependantKey) {
      throw new TypeError('No argument');
    }
    if (dependantKey === null) {
      throw new TypeError('Null argument');
    }

    return computedUnsafe(dependantKey, {
      get(rawValue) {
        // Check for null/undefined values
        if (A(['undefined', 'null']).indexOf(typeOf(rawValue)) !== -1) {
          return NaN;
        }
        else {
          // Handle some unexpected behavior for empty-string property keys
          // related:
          //  https://github.com/emberjs/ember.js/commit/b7e82f43c3475ee7b166a2570b061f08c6c6c0f3#diff-22c6caff03531b3e718e9a8d82180833R31
          if ('string' === typeof rawValue && rawValue.length === 0) {
            return NaN;
          }
          else {
            return parseFunction(rawValue);
          }
        }
      },
      set(val, rawValue) {
        //setter
        //respect the type of the dependent property
        switch (typeOf(rawValue)) {
          case 'number':
            this.set(dependantKey, parseFloat(val));
            break;
          case 'boolean':
            switch(typeOf(val)) {
              case 'string':
                this.set(dependantKey, val.toLowerCase() === 'true');
                break;
              case 'number':
                this.set(dependantKey, val !== 0);
                break;
              default:
                var msg = fmt('Can\'t transform value of type %@ into a boolean', typeOf(val));
                throw new TypeError(msg);
            }
            break;
          default:
            this.set(dependantKey, val.toString());
            break;
        }
        return val;
      }
    });
  };
}

export function fmt(str, formats) {
  // first, replace any ORDERED replacements.
   let idx = 0; // the current index for non-numerical replacements
   return str.replace(/%@([0-9]+)?/g, (s, argIndex) => {
     let i = argIndex ? parseInt(argIndex, 10) - 1 : idx++;
     let r = i < formats.length ? formats[i] : undefined;
     return typeof r === 'string' ? r : r === null ? '(null)' : r === undefined ? '' : '' + r;
   });
}