Task = function() {
  var task = this;
  var index = 0;
  var steps = [];

  /**
   * Mainly used internally for tests.
   *
   * @returns {TaskMdl} the mongoose model for the tasks
   */
  task.getTaskCollection = function() {
    return TaskMdl;
  };

  /**
   * @see dbUtils.initModel
   *
   * @param modelName The intended name of the model
   * @param schema The schema associated with this model
   * @returns {Task}
   */
  task.initModel = function(modelName, schema) {
    dbUtils.initModel(modelName, schema);

    return task;
  };

  /**
   * Adds an update step (updateObj) to the steps queue
   * and increments the index.
   *
   * @param model the model or document to update
   * @param condition the condition or data for this update
   * @param data the data for this update
   *
   * @returns {Task}
   */
  task.update = function(model, condition, data) {
    if (!data) {
      if (!validDoc(model)) throw new Error("Invalid doc");

      data = condition;
      condition = {_id: model._id};
      model = model.constructor;
    }
    if (!validModel(model)) throw new Error("Invalid model");
    if (!isObject(condition)) throw new Error("Invalid Condition");
    if (!isObject(data)) throw new Error("Invalid data");

    var updateObj = {
      index: index
      , type: UPDATE
      , state: INITIAL
      , name: getModelName(model)
      , condition: xcode(condition)
      , data: xcode(data)
    };

    steps.push(updateObj);
    index++;

    return task;
  };

  /**
   * Adds a save step (saveObj) to the steps queue
   * and increments the index.
   *
   * @param model the model we're saving to or document to save
   * @param doc the object to be saved
   *
   * @returns {Task}
   */
  task.save = function(model, doc) {
    if (!doc) {
      if (!validDoc(model)) throw new Error("Invalid doc");

      doc = model.toObject();
      model = model.constructor;
    }
    else if (validDoc(doc)) doc = doc.toObject();

    if (!validModel(model)) throw new Error("Invalid Model");
    if (!isObject(doc)) throw new Error("Invalid doc");

    var saveObj = {
      index: index
      , type: SAVE
      , state: INITIAL
      , name: getModelName(model)
      , data: xcode(doc)
    };

    steps.push(saveObj);
    index++;

    return task;
  };

  /**
   * Adds a remove step (removeObj) to the steps queue
   * and increments the index.
   *
   * @param model the model we're removing from or document to remove
   * @param condition the condition for removal
   *
   * @returns {Task}
   */
  task.remove = function(model, condition) {
    if (!condition) {
      if (!validDoc(model)) throw new Error("Invalid doc");

      condition = {_id: model._id};
      model = model.constructor;
    }
    if (!validModel(model)) throw new Error("Invalid Model");
    if (!isObject(condition)) throw new Error("Invalid Condition");

    var removeObj = {
      index: index
      , type: REMOVE
      , state: INITIAL
      , name: getModelName(model)
      , condition: xcode(condition)
    };

    steps.push(removeObj);
    index++;

    return task;
  };

  /**
   * Adds options to an update step.
   *
   * @param options the options to be added
   *
   * @returns {Task}
   */
  task.options = function(options) {
    if (!steps.length) throw new Error("Can't set options on non-existing task");
    if (!isObject(options)) throw new Error("Invalid Options");

    var obj = steps[steps.length - 1];

    if (obj.type !== UPDATE) {
      throw new Error("the " + obj.type + " function does not accept options");
    }

    obj.options = options;
    return task;
  };

  /**
   * Adds a saveFile step (saveFileObj) to the steps queue
   * and increments the index.
   *
   * @param filePath path of the file to be saved
   * @param options options for saving  the file
   *
   * @returns {Task}
   */
  task.saveFile = function (filePath, options) {
    if (!filePath) throw new Error("File path is required and must be a string");
    if (options && !isObject(options)) throw new Error("options must be an object");

    var saveFileObj = {
      type: FILE_SAVE
      , index: index
      , state: INITIAL
      , data: {
        file_path: filePath
      }
      , options: xcode(options)
    };

    steps.push(saveFileObj);
    index++;

    return task;
  };

  /**
   * Adds a removeFile step (removeFileObj) to the steps queue
   * and increments the index.
   *
   * @param options options for removing  the file
   *
   * @returns {Task}
   */
  task.removeFile = function (options) {
    if (!isObject(options)) throw new Error("options required. Must be an object");

    var removeFileObj = {
      type: FILE_REMOVE
      , index: index
      , state: INITIAL
      , options: xcode(options)
    };

    steps.push(removeFileObj);
    index++;

    return task;
  };

  /**
   * Runs a task. This function saves the steps to
   * the db and proceeds to complete each step. If
   * any of the steps fail, all previously completed
   * steps get rolled back and the causal error is
   * returned through a promise
   *
   * @options options to run with
   *
   * @returns a promise
   */
  task.run = function(options){
    var chain = Promise.resolve();
    var dbTask = new TaskMdl({steps: steps});
    steps = [];
    index = 0;
    var results = [];

    return dbTask.save().then(function (_task) {
      _task.steps.forEach(function (step) {
        chain = chain.then(function () {
          if(options) step.useMongoose = options.useMongoose;

          return getResolveFunc(step)(step, _task, results);
        });
      });

      return chain.then(function () {
        var gfs = Grid(mongoose.connection.db);
        var removeChain = dbUtils.makeRemoveChain(_task, gfs);

        return removeChain.then(function () {
          return _task.constructor.collection.deleteOne({_id: _task._id})
            .then(function () {
              return Promise.resolve(results);
            });
        });
      }).catch(function (err) {
        return Roller.rollOne(_task).then(function () {
          throw err;
        });
      });
    });
  };
}
The task class. It contains all the functions associated
with a task. Enables edits to be queued as a series of
steps and run, in the order they were queued, in a
way that allows the edits to be rolled back in the event
of a failure.

@constructor