lib/command_factory.js
function iterate(accumulator, combos, cmd, callback)
{
  var params;

  // initial call might not have accumulator
  if (arguments.length == 3)
  {
    callback    = cmd;
    cmd         = combos;
    combos      = accumulator;
    accumulator = [];
  }

  // resolve params from combo reference
  if (typeof combos == 'string')
  {
    // only work with arrays and object this way
    params = null;

    // get elements one by one from the array
    // consider number of elements in `accumulator`
    // as number of processed from referenced array
    if (Array.isArray(this[combos]) && this[combos][accumulator.length])
    {
      params = {};
      params[combos] = this[combos][accumulator.length];
    }

    // if object gets referenced
    // create list of combinations out of it
    // and use array logic to resolve
    // current iteration params
    else if (typeOf(this[combos]) == 'object')
    {
      params = cartesian(clone(this[combos]))[accumulator.length];
    }
  }
  else
  {
    // get another combination
    params = combos.shift();
  }

  // when done with the combos
  // invoke callback
  if (!params)
  {
    // if it's a single element, return it as is
    callback(null, (accumulator && accumulator.length == 1) ? accumulator[0] : accumulator);
    return;
  }

  // if command name doesn't as state property
  // consider it as standalone command
  runCommand.call(this, params, this[cmd] || cmd, function(err, output)
  {
    accumulator.push(output);

    if (err)
    {
      callback(err, accumulator);
      return;
    }

    // rinse, repeat
    iterate.call(this, accumulator, combos, cmd, callback);
  }.bind(this));
}
function iterate(accumulator, combos, cmd, callback)
{
  var params;

  // initial call might not have accumulator
  if (arguments.length == 3)
  {
    callback    = cmd;
    cmd         = combos;
    combos      = accumulator;
    accumulator = [];
  }

  // resolve params from combo reference
  if (typeof combos == 'string')
  {
    // only work with arrays and object this way
    params = null;

    // get elements one by one from the array
    // consider number of elements in `accumulator`
    // as number of processed from referenced array
    if (Array.isArray(this[combos]) && this[combos][accumulator.length])
    {
      params = {};
      params[combos] = this[combos][accumulator.length];
    }

    // if object gets referenced
    // create list of combinations out of it
    // and use array logic to resolve
    // current iteration params
    else if (typeOf(this[combos]) == 'object')
    {
      params = cartesian(clone(this[combos]))[accumulator.length];
    }
  }
  else
  {
    // get another combination
    params = combos.shift();
  }

  // when done with the combos
  // invoke callback
  if (!params)
  {
    // if it's a single element, return it as is
    callback(null, (accumulator && accumulator.length == 1) ? accumulator[0] : accumulator);
    return;
  }

  // if command name doesn't as state property
  // consider it as standalone command
  runCommand.call(this, params, this[cmd] || cmd, function(err, output)
  {
    accumulator.push(output);

    if (err)
    {
      callback(err, accumulator);
      return;
    }

    // rinse, repeat
    iterate.call(this, accumulator, combos, cmd, callback);
  }.bind(this));
}
function iterate ( accumulator , combos , cmd , callback ) { var params ; // initial call might not have accumulator if ( arguments . length == 3 ) { callback = cmd ; cmd = combos ; combos = accumulator ; accumulator = [ ] ; } // resolve params from combo reference if ( typeof combos == 'string' ) { // only work with arrays and object this way params = null ; // get elements one by one from the array // consider number of elements in `accumulator` // as number of processed from referenced array if ( Array . isArray ( this [ combos ] ) && this [ combos ] [ accumulator . length ] ) { params = { } ; params [ combos ] = this [ combos ] [ accumulator . length ] ; } // if object gets referenced // create list of combinations out of it // and use array logic to resolve // current iteration params else if ( typeOf ( this [ combos ] ) == 'object' ) { params = cartesian ( clone ( this [ combos ] ) ) [ accumulator . length ] ; } } else { // get another combination params = combos . shift ( ) ; } // when done with the combos // invoke callback if ( ! params ) { // if it's a single element, return it as is callback ( null , ( accumulator && accumulator . length == 1 ) ? accumulator [ 0 ] : accumulator ) ; return ; } // if command name doesn't as state property // consider it as standalone command runCommand . call ( this , params , this [ cmd ] || cmd , function ( err , output ) { accumulator . push ( output ) ; if ( err ) { callback ( err , accumulator ) ; return ; } // rinse, repeat iterate . call ( this , accumulator , combos , cmd , callback ) ; } . bind ( this ) ) ; }
Runs provided command with each of the combinations

@private
@param   {array} [accumulator] – accumulates commands output
@param   {array|string} combos – list of combinations
@param   {string|array} cmd - command to run
@param   {function} callback - invoked after commands finished executing
@returns {void}
Runs provided command with each of the combinations