Source: functions/resolver.js



/**
 * A function for resolving a value from a given value. Typically used to
 * transform an object into one of it's properties.
 *
 * @callback propertyResolverCallback
 * @param {Any} model -
 *    The model to use to resolve a value.
 * @return {Any} -
 *    The resolved value.
 * @see Rekord.createPropertyResolver
 */


/**
 * An expression which resolves a value from another value.
 *
 * ```javascript
 * // {age: 6, name: 'x', user: {first: 'tom'}}
 * 'age'                    // age property of an object
 * 'user.first'             // sub property
 * '{age}, {user.first}'    // a formatted string built from object values
 * function(a) {}           // a function which returns a value itself
 * ['age', 'name']          // multiple properties joined with a delimiter
 * {age:null, user:'first'} // multiple properties joined with a delimiter including a sub property
 * ```
 *
 * @typedef {String|Function|Array|Object} propertyResolverInput
 */

var NumberResolvers = {};

function saveNumberResolver(name, numbers)
{
  var resolver = createNumberResolver( numbers );

  NumberResolvers[ name ] = resolver;

  return resolver;
}

function createNumberResolver(numbers)
{
  var resolver = createPropertyResolver( numbers );

  if ( isString( numbers ) && numbers in NumberResolvers )
  {
    return NumberResolvers[ numbers ];
  }

  return function resolveNumber(model)
  {
    return parseFloat( resolver( model ) );
  };
}

var PropertyResolvers = {};

function savePropertyResolver(name, properties, delim)
{
  var resolver = createPropertyResolver( properties, delim );

  PropertyResolvers[ name ] = resolver;

  return resolver;
}

/**
 * Creates a function which resolves a value from another value given an
 * expression. This is often used to get a property value of an object.
 *
 * ```javascript
 * // x = {age: 6, name: 'tom', user: {first: 'jack'}}
 * createPropertyResolver()( x )                          // x
 * createPropertyResolver( 'age' )( x )                   // 6
 * createPropertyResolver( 'user.first' )( x )            // 'jack'
 * createPropertyResolver( '{name} & {user.first}')( x )  // 'tom & jack'
 * createPropertyResolver( ['name', 'age'] )( x )         // 'tom,6'
 * createPropertyResolver( ['name', 'age'], ' is ' )( x ) // 'tom is 6'
 * createPropertyResolver( {age:null, user:'first'})( x ) // '6,jack'
 * ```
 *
 * @memberof Rekord
 * @param {propertyResolverInput} [properties] -
 *    The expression which converts one value into another.
 * @param {String} [delim=','] -
 *    A delimiter to use to join multiple properties into a string.
 * @return {propertyResolverCallback} -
 *    A function to take values and resolve new ones.
 */
function createPropertyResolver(properties, delim)
{
  if ( isFunction( properties ) )
  {
    return properties;
  }
  else if ( isString( properties ) )
  {
    if ( properties in PropertyResolvers )
    {
      return PropertyResolvers[ properties ];
    }

    if ( properties.indexOf('{') !== -1 )
    {
      return function resolveFormatted(model)
      {
        return format( properties, model );
      };
    }
    else if ( properties.indexOf('.') !== -1 )
    {
      return function resolveExpression(model)
      {
        return parse( properties, model );
      };
    }
    else
    {
      return function resolveProperty(model)
      {
        return model ? model[ properties ] : undefined;
      };
    }
  }
  else if ( isArray( properties ) )
  {
    return function resolveProperties(model)
    {
      return pull( model, properties ).join( delim );
    };
  }
  else if ( isObject( properties ) )
  {
    var propsArray = [];
    var propsResolver = [];

    for (var prop in properties)
    {
      propsArray.push( prop );
      propsResolver.push( createPropertyResolver( properties[ prop ], delim ) );
    }

    return function resolvePropertyObject(model)
    {
      var pulled = [];

      for (var i = 0; i < prop.length; i++)
      {
        pulled.push( propsResolver[ i ]( model[ propsArray[ i ] ] ) );
      }

      return pulled.join( delim );
    };
  }
  else
  {
    return function resolveNone(model)
    {
      return model;
    };
  }
}