<commit_before>'use strict';

var N1qlQuery = require('./n1qlquery');

function _respRead(callback) {
  return function(resp) {
    resp.setEncoding('utf8');
    var strBuffer = '';
    resp.on('data', function (data) {
      strBuffer += data;
    });
    resp.on('end', function () {
      callback(null, resp, strBuffer);
    });
    resp.on('error', function (err) {
      callback(err, resp, null);
    });
  };
}

/**
 * @class
 * A class for performing management operations against a bucket. This class
 * should not be instantiated directly, but instead through the use of the
 * {@link Bucket#manager} method instead.
 *
 * @param bucket
 *
 * @private
 *
 * @since 2.0.0
 * @committed
 */
function BucketManager(bucket) {
  this._bucket = bucket;
}

/**
 * Method for performing a http mgmt operation using the underlying bucket.
 *
 * @param path
 * @param method
 * @param callback
 *
 * @private
 * @ignore
 */
BucketManager.prototype._mgmtRequest = function(path, method, callback) {
  var b = this._bucket;
  b._maybeInvoke(b._mgmtRequest.bind(b), [path, method, callback]);
};

/**
 * Method for performing a http capi request using the underlying bucket.
 *
 * @param path
 * @param method
 * @param callback
 *
 * @private
 * @ignore
 */
BucketManager.prototype._capiRequest = function(path, method, callback) {
  var b = this._bucket;
  b._maybeInvoke(b._capiRequest.bind(b), [path, method, callback]);
};

/**
 * Retrieves a list of all design documents registered to a bucket.
 *
 * @param callback
 *
 * @since 2.0.0
 * @committed
 */
BucketManager.prototype.getDesignDocuments = function(callback) {
  var path = 'pools/default/buckets/' + this._bucket._name + '/ddocs';

  this._mgmtRequest(path, 'GET', function(err, httpReq) {
    if (err) {
      return callback(err, null);
    }

    httpReq.on('response', _respRead(function (err, resp, data) {
      if (err) {
        return callback(err);
      }
      if (resp.statusCode !== 200) {
        var errData = JSON.parse(data);
        return callback(new Error(errData.reason), null);
      }
      var ddocData = JSON.parse(data);
      var ddocs = {};
      for (var i = 0; i < ddocData.rows.length; ++i) {
        var ddoc = ddocData.rows[i].doc;
        var ddocName = ddoc.meta.id.substr(8);
        ddocs[ddocName] = ddoc.json;
      }
      callback(null, ddocs);
    }));
    httpReq.end();
  });
};

/**
 * Registers a design document to this bucket, failing if it already exists.
 *
 * @param name
 * @param data
 * @param callback
 *
 * @since 2.0.0
 * @committed
 */
BucketManager.prototype.insertDesignDocument = function(name, data, callback) {
  var self = this;
  this.getDesignDocument(name, function(err, res) {
    if (!err) {
      return callback(new Error('design document already exists'), null);
    }
    self.upsertDesignDocument(name, data, callback);
  });
};

/**
 * Registers a design document to this bucket, overwriting any existing
 * design document that was previously registered.
 *
 * @param name
 * @param data
 * @param callback
 *
 * @since 2.0.0
 * @committed
 */
BucketManager.prototype.upsertDesignDocument = function(name, data, callback) {
  var path = '_design/' + name;

  this._capiRequest(path, 'PUT', function(err, httpReq) {
    if (err) {
      return callback(err, null);
    }

    httpReq.on('response', _respRead(function(err, resp, data) {
      if (err) {
        return callback(err);
      }
      if (resp.statusCode !== 201) {
        var errData = JSON.parse(data);
        return callback(new Error(errData.reason), null);
      }
      callback(null, true);
    }));
    httpReq.write(JSON.stringify(data, function(key, value) {
      if (value instanceof Function) {
        return value.toString();
      }
      return value;
    }));
    httpReq.end();
  });
};

/**
 * Retrieves a specific design document from this bucket.
 *
 * @param name
 * @param callback
 *
 * @since 2.0.0
 * @committed
 */
BucketManager.prototype.getDesignDocument = function(name, callback) {
  var path = '_design/' + name;

  this._capiRequest(path, 'GET', function(err, httpReq) {
    if (err) {
      return callback(err, null);
    }

    httpReq.on('response', _respRead(function(err, resp, data) {
      if (err) {
        return callback(err);
      }
      var ddocData = JSON.parse(data);
      if (resp.statusCode !== 200) {
        var errData = JSON.parse(data);
        return callback(new Error(errData.reason), null);
      }
      callback(null, ddocData);
    }));
    httpReq.end();
  });
};

/**
 * Unregisters a design document from this bucket.
 *
 * @param name
 * @param callback
 *
 * @since 2.0.0
 * @committed
 */
BucketManager.prototype.removeDesignDocument = function(name, callback) {
  var path = '_design/' + name;

  this._capiRequest(path, 'DELETE', function(err, httpReq) {
    if (err) {
      return callback(err, null);
    }

    httpReq.on('response', _respRead(function(err, resp, data) {
      if (err) {
        return callback(err);
      }
      if (resp.statusCode !== 200) {
        var errData = JSON.parse(data);
        return callback(new Error(errData.reason), null);
      }
      callback(null, true);
    }));
    httpReq.end();
  });
};

/**
 * Flushes the cluster, deleting all data stored within this bucket.  Note that
 * this method requires the Flush permission to be enabled on the bucket from
 * the management console before it will work.
 *
 * @param callback
 *
 * @since 2.0.0
 * @committed
 */
BucketManager.prototype.flush = function(callback) {
  var path = 'pools/default/buckets/' +
      this._bucket._name + '/controller/doFlush';

  this._mgmtRequest(path, 'POST', function(err, httpReq) {
    if (err) {
      return callback(err, null);
    }

    httpReq.on('response', _respRead(function(err, resp, data) {
      if (err) {
        return callback(err);
      }
      if (resp.statusCode !== 200) {
        var errData = JSON.parse(data);
        return callback(new Error(errData.reason), null);
      }
      callback(null, true);
    }));
    httpReq.end();
  });
};

/**
 * Method for creating GSI indexes
 *
 * @param options
 * @param options.name
 * @param options.fields
 * @param options.deferred
 * @param callback
 *
 * @private
 * @ignore
 */
BucketManager.prototype._createIndex = function(options, callback) {
  if (!options.fields) {
    options.fields = [];
  }

  var qs = '';

  if (options.fields.length === 0) {
    qs += 'CREATE PRIMARY INDEX';
  } else {
    qs += 'CREATE INDEX';
  }
  if (options.name !== '') {
    qs += ' `' + options.name + '`';
  }
  qs += ' ON `' + this._bucket.name + '`';
  if (options.fields.length > 0) {
    qs += '(';
    for (var i = 0; i < options.fields.length; ++i) {
      if (i > 0) {
        qs += ', ';
      }
      qs += '`' + options.fields[i] + '`';
    }
    qs += ')';
  }
  if (options.deferred) {
    qs += ' WITH {"defer_build": true}';
  }

  this._bucket.query(N1qlQuery.fromString(qs), function(err, rows) {
    if (err) {
      if (err.message.indexOf('already exist') !== -1 &&
          options.ignoreIfExists) {
        callback(null);
        return;
      }

      callback(err);
      return;
    }

    callback(null);
  });
};

/**
 * Creates a non-primary GSI index from a name and list of fields.
 *
 * @param indexName
 * @param fields
 * @param options
 * @param options.ignoreIfExists
 * @param options.deferred
 * @param callback
 *
 * @since 2.1.6
 * @uncommitted
 */
BucketManager.prototype.createIndex = function(indexName, fields, options, callback) {
  if (options instanceof Function) {
    callback = arguments[2];
    options = undefined;
  }
  if (!options) {
    options = {};
  }

  return this._createIndex({
    name: indexName,
    fields: fields,
    ignoreIfExists: options.ignoreIfExists,
    deferred: options.deferred
  }, callback);
};

/**
 * Creates a primary GSI index with an optional name
 * @param options
 * @param options.ignoreIfExists
 * @param options.deferred
 * @param callback
 *
 * @since 2.1.6
 * @uncommitted
 */
BucketManager.prototype.createPrimaryIndex = function(options, callback) {
  if (options instanceof Function) {
    callback = arguments[0];
    options = undefined;
  }
  if (!options) {
    options = {};
  }

  return this._createIndex({
    name: options.name,
    ignoreIfExists: options.ignoreIfExists,
    deferred: options.deferred
  }, callback);
};

/**
 * Method for dropping GSI indexes
 *
 * @param options
 * @param options.name
 * @param callback
 *
 * @private
 * @ignore
 */
BucketManager.prototype._dropIndex = function(options, callback) {
  var qs = '';

  if (!options.name) {
    qs += 'DROP PRIMARY INDEX `' + this._bucket.name + '`';
  } else {
    qs += 'DROP INDEX `' + this._bucket.name + '`.`' + options.name + '`';
  }

  this._bucket.query(N1qlQuery.fromString(qs), function(err) {
    if (err) {
      if (err.message.indexOf('not found') !== -1 &&
          options.ignoreIfNotExists) {
        callback(null);
        return;
      }

      callback(err);
      return;
    }

    callback(null);
  });
};

/**
 * Drops a specific GSI index by name.
 *
 * @param indexName
 * @param options
 * @param options.ignoreIfNotExists
 * @param callback
 *
 * @since 2.1.6
 * @uncommitted
 */
BucketManager.prototype.dropIndex = function(indexName, options, callback) {
  if (options instanceof Function) {
    callback = arguments[1];
    options = undefined;
  }
  if (!options) {
    options = {};
  }

  return this._dropIndex({
    name: indexName,
    ignoreIfNotExists: options.ignoreIfNotExists
  }, callback);
};

/**
 * Drops a primary GSI index
 *
 * @param options
 * @param options.ignoreIfNotExists
 * @param callback
 *
 * @since 2.1.6
 * @uncommitted
 */
BucketManager.prototype.dropPrimaryIndex = function(options, callback) {
  if (options instanceof Function) {
    callback = arguments[1];
    options = undefined;
  }
  if (!options) {
    options = {};
  }

  return this._dropIndex({
    name: options.name,
    ignoreIfNotExists: options.ignoreIfNotExists
  }, callback);
};

/**
 * Retreives a list of the indexes currently configured on the cluster.
 *
 * @param callback
 *
 * @since 2.1.6
 * @uncommitted
 */
BucketManager.prototype.getIndexes = function(callback) {
  var qs = 'SELECT `indexes`.* FROM system:indexes';
  this._bucket.query(N1qlQuery.fromString(qs), function(err, rows) {
    if (err) {
      callback(err, null);
      return;
    }

    callback(null, rows);
  });
};

/**
 * Builds any indexes that were previously created with the deferred attribute.
 *
 * @param callback
 *
 * @since 2.1.6
 * @uncommitted
 */
BucketManager.prototype.buildDeferredIndexes = function(callback) {
  this.getIndexes(function(err, indexes) {
    if (err) {
      callback(err, null);
      return;
    }

    var deferredList = [];
    for (var i = 0; i < indexes.length; ++i) {
      if (indexes[i].state === 'deferred' || indexes[i].state === 'pending') {
        deferredList.push(indexes[i].name);
      }
    }

    if (deferredList.length === 0) {
      callback(null, deferredList);
      return;
    }

    var qs = '';
    qs += 'BUILD INDEX ON `' + this._bucket.name + '`('
    for (var j = 0; j < deferredList.length; ++i) {
      if (j > 0) {
        qs += ', ';
      }
      qs += '`' + deferredList[j] + '`';
    }

    this._bucket.query(N1qlQuery.fromString(qs), function(err) {
      if (err) {
        callback(err, deferredList);
        return;
      }

      callback(null, deferredList);
    });
  });
};

/**
 * Method for checking a list of indexes to see if they are active
 *
 * @param checkList
 * @param callback
 *
 * @private
 * @ignore
 */
BucketManager.prototype._checkIndexesActive = function(checkList, callback) {
  this.getIndexes(function(err, indexes) {
    if (err) {
      callback(err, false);
      return;
    }

    var checkIndexes = [];
    for (var i = 0; i < indexes.length; i++) {
      if (checkList.indexOf(indexes[i].name) !== -1) {
        checkIndexes.push(indexes[i]);
      }
    }

    if (checkIndexes.length != checkList.length) {
      callback(new Error('index not found'), false);
      return;
    }

    for (var j = 0; j < checkIndexes.length; ++j) {
      if (checkIndexes[j].state !== 'online') {
        callback(null, false);
        return;
      }
    }

    callback(null, true);
  });
};

/**
 * Watches a list of indexes; waiting for them to become available for use.
 *
 * @param watchList
 * @param options
 * @param options.timeout Timeout in millseconds
 * @param callback
 *
 * @since 2.1.6
 * @uncommitted
 */
BucketManager.prototype.watchIndexes = function(watchList, options, callback) {
  if (options instanceof Function) {
    callback = arguments[1];
    options = undefined;
  }
  if (!options) {
    options = {};
  }

  var timeoutTime = 0;
  if (options.timeout) {
    timeoutTime = Date.now() + options.timeout;
  }

  var self = this;
  var curInterval = 50;
  (function _checkIndexes() {
    self._checkIndexesActive(watchList, function(err, allActive) {
      if (err) {
        callback(err);
        return;
      }

      if (allActive) {
        callback(null);
        return;
      }

      if (timeoutTime > 0 && Date.now() + curInterval > timeoutTime) {
        callback(new Error('timeout'));
        return;
      }

      setTimeout(function() {
        curInterval += 500;
        if (curInterval > 1000) {
          curInterval = 1000;
        }

        _checkIndexes();
      }, curInterval);
    });
  })();
};

module.exports = BucketManager;
<commit_msg>Fixed an issue with bucketmgr.js file formatting.<commit_after>'use strict';

var N1qlQuery = require('./n1qlquery');

function _respRead(callback) {
  return function(resp) {
    resp.setEncoding('utf8');
    var strBuffer = '';
    resp.on('data', function (data) {
      strBuffer += data;
    });
    resp.on('end', function () {
      callback(null, resp, strBuffer);
    });
    resp.on('error', function (err) {
      callback(err, resp, null);
    });
  };
}

/**
 * @class
 * A class for performing management operations against a bucket. This class
 * should not be instantiated directly, but instead through the use of the
 * {@link Bucket#manager} method instead.
 *
 * @param bucket
 *
 * @private
 *
 * @since 2.0.0
 * @committed
 */
function BucketManager(bucket) {
  this._bucket = bucket;
}

/**
 * Method for performing a http mgmt operation using the underlying bucket.
 *
 * @param path
 * @param method
 * @param callback
 *
 * @private
 * @ignore
 */
BucketManager.prototype._mgmtRequest = function(path, method, callback) {
  var b = this._bucket;
  b._maybeInvoke(b._mgmtRequest.bind(b), [path, method, callback]);
};

/**
 * Method for performing a http capi request using the underlying bucket.
 *
 * @param path
 * @param method
 * @param callback
 *
 * @private
 * @ignore
 */
BucketManager.prototype._capiRequest = function(path, method, callback) {
  var b = this._bucket;
  b._maybeInvoke(b._capiRequest.bind(b), [path, method, callback]);
};

/**
 * Retrieves a list of all design documents registered to a bucket.
 *
 * @param callback
 *
 * @since 2.0.0
 * @committed
 */
BucketManager.prototype.getDesignDocuments = function(callback) {
  var path = 'pools/default/buckets/' + this._bucket._name + '/ddocs';

  this._mgmtRequest(path, 'GET', function(err, httpReq) {
    if (err) {
      return callback(err, null);
    }

    httpReq.on('response', _respRead(function (err, resp, data) {
      if (err) {
        return callback(err);
      }
      if (resp.statusCode !== 200) {
        var errData = JSON.parse(data);
        return callback(new Error(errData.reason), null);
      }
      var ddocData = JSON.parse(data);
      var ddocs = {};
      for (var i = 0; i < ddocData.rows.length; ++i) {
        var ddoc = ddocData.rows[i].doc;
        var ddocName = ddoc.meta.id.substr(8);
        ddocs[ddocName] = ddoc.json;
      }
      callback(null, ddocs);
    }));
    httpReq.end();
  });
};

/**
 * Registers a design document to this bucket, failing if it already exists.
 *
 * @param name
 * @param data
 * @param callback
 *
 * @since 2.0.0
 * @committed
 */
BucketManager.prototype.insertDesignDocument = function(name, data, callback) {
  var self = this;
  this.getDesignDocument(name, function(err, res) {
    if (!err) {
      return callback(new Error('design document already exists'), null);
    }
    self.upsertDesignDocument(name, data, callback);
  });
};

/**
 * Registers a design document to this bucket, overwriting any existing
 * design document that was previously registered.
 *
 * @param name
 * @param data
 * @param callback
 *
 * @since 2.0.0
 * @committed
 */
BucketManager.prototype.upsertDesignDocument = function(name, data, callback) {
  var path = '_design/' + name;

  this._capiRequest(path, 'PUT', function(err, httpReq) {
    if (err) {
      return callback(err, null);
    }

    httpReq.on('response', _respRead(function(err, resp, data) {
      if (err) {
        return callback(err);
      }
      if (resp.statusCode !== 201) {
        var errData = JSON.parse(data);
        return callback(new Error(errData.reason), null);
      }
      callback(null, true);
    }));
    httpReq.write(JSON.stringify(data, function(key, value) {
      if (value instanceof Function) {
        return value.toString();
      }
      return value;
    }));
    httpReq.end();
  });
};

/**
 * Retrieves a specific design document from this bucket.
 *
 * @param name
 * @param callback
 *
 * @since 2.0.0
 * @committed
 */
BucketManager.prototype.getDesignDocument = function(name, callback) {
  var path = '_design/' + name;

  this._capiRequest(path, 'GET', function(err, httpReq) {
    if (err) {
      return callback(err, null);
    }

    httpReq.on('response', _respRead(function(err, resp, data) {
      if (err) {
        return callback(err);
      }
      var ddocData = JSON.parse(data);
      if (resp.statusCode !== 200) {
        var errData = JSON.parse(data);
        return callback(new Error(errData.reason), null);
      }
      callback(null, ddocData);
    }));
    httpReq.end();
  });
};

/**
 * Unregisters a design document from this bucket.
 *
 * @param name
 * @param callback
 *
 * @since 2.0.0
 * @committed
 */
BucketManager.prototype.removeDesignDocument = function(name, callback) {
  var path = '_design/' + name;

  this._capiRequest(path, 'DELETE', function(err, httpReq) {
    if (err) {
      return callback(err, null);
    }

    httpReq.on('response', _respRead(function(err, resp, data) {
      if (err) {
        return callback(err);
      }
      if (resp.statusCode !== 200) {
        var errData = JSON.parse(data);
        return callback(new Error(errData.reason), null);
      }
      callback(null, true);
    }));
    httpReq.end();
  });
};

/**
 * Flushes the cluster, deleting all data stored within this bucket.  Note that
 * this method requires the Flush permission to be enabled on the bucket from
 * the management console before it will work.
 *
 * @param callback
 *
 * @since 2.0.0
 * @committed
 */
BucketManager.prototype.flush = function(callback) {
  var path = 'pools/default/buckets/' +
      this._bucket._name + '/controller/doFlush';

  this._mgmtRequest(path, 'POST', function(err, httpReq) {
    if (err) {
      return callback(err, null);
    }

    httpReq.on('response', _respRead(function(err, resp, data) {
      if (err) {
        return callback(err);
      }
      if (resp.statusCode !== 200) {
        var errData = JSON.parse(data);
        return callback(new Error(errData.reason), null);
      }
      callback(null, true);
    }));
    httpReq.end();
  });
};

/**
 * Method for creating GSI indexes
 *
 * @param options
 * @param options.name
 * @param options.fields
 * @param options.deferred
 * @param callback
 *
 * @private
 * @ignore
 */
BucketManager.prototype._createIndex = function(options, callback) {
  if (!options.fields) {
    options.fields = [];
  }

  var qs = '';

  if (options.fields.length === 0) {
    qs += 'CREATE PRIMARY INDEX';
  } else {
    qs += 'CREATE INDEX';
  }
  if (options.name !== '') {
    qs += ' `' + options.name + '`';
  }
  qs += ' ON `' + this._bucket.name + '`';
  if (options.fields.length > 0) {
    qs += '(';
    for (var i = 0; i < options.fields.length; ++i) {
      if (i > 0) {
        qs += ', ';
      }
      qs += '`' + options.fields[i] + '`';
    }
    qs += ')';
  }
  if (options.deferred) {
    qs += ' WITH {"defer_build": true}';
  }

  this._bucket.query(N1qlQuery.fromString(qs), function(err, rows) {
    if (err) {
      if (err.message.indexOf('already exist') !== -1 &&
          options.ignoreIfExists) {
        callback(null);
        return;
      }

      callback(err);
      return;
    }

    callback(null);
  });
};

/**
 * Creates a non-primary GSI index from a name and list of fields.
 *
 * @param indexName
 * @param fields
 * @param options
 * @param options.ignoreIfExists
 * @param options.deferred
 * @param callback
 *
 * @since 2.1.6
 * @uncommitted
 */
BucketManager.prototype.createIndex = function(indexName, fields, options, callback) {
  if (options instanceof Function) {
    callback = arguments[2];
    options = undefined;
  }
  if (!options) {
    options = {};
  }

  return this._createIndex({
    name: indexName,
    fields: fields,
    ignoreIfExists: options.ignoreIfExists,
    deferred: options.deferred
  }, callback);
};

/**
 * Creates a primary GSI index with an optional name
 * @param options
 * @param options.ignoreIfExists
 * @param options.deferred
 * @param callback
 *
 * @since 2.1.6
 * @uncommitted
 */
BucketManager.prototype.createPrimaryIndex = function(options, callback) {
  if (options instanceof Function) {
    callback = arguments[0];
    options = undefined;
  }
  if (!options) {
    options = {};
  }

  return this._createIndex({
    name: options.name,
    ignoreIfExists: options.ignoreIfExists,
    deferred: options.deferred
  }, callback);
};

/**
 * Method for dropping GSI indexes
 *
 * @param options
 * @param options.name
 * @param callback
 *
 * @private
 * @ignore
 */
BucketManager.prototype._dropIndex = function(options, callback) {
  var qs = '';

  if (!options.name) {
    qs += 'DROP PRIMARY INDEX `' + this._bucket.name + '`';
  } else {
    qs += 'DROP INDEX `' + this._bucket.name + '`.`' + options.name + '`';
  }

  this._bucket.query(N1qlQuery.fromString(qs), function(err) {
    if (err) {
      if (err.message.indexOf('not found') !== -1 &&
          options.ignoreIfNotExists) {
        callback(null);
        return;
      }

      callback(err);
      return;
    }

    callback(null);
  });
};

/**
 * Drops a specific GSI index by name.
 *
 * @param indexName
 * @param options
 * @param options.ignoreIfNotExists
 * @param callback
 *
 * @since 2.1.6
 * @uncommitted
 */
BucketManager.prototype.dropIndex = function(indexName, options, callback) {
  if (options instanceof Function) {
    callback = arguments[1];
    options = undefined;
  }
  if (!options) {
    options = {};
  }

  return this._dropIndex({
    name: indexName,
    ignoreIfNotExists: options.ignoreIfNotExists
  }, callback);
};

/**
 * Drops a primary GSI index
 *
 * @param options
 * @param options.ignoreIfNotExists
 * @param callback
 *
 * @since 2.1.6
 * @uncommitted
 */
BucketManager.prototype.dropPrimaryIndex = function(options, callback) {
  if (options instanceof Function) {
    callback = arguments[1];
    options = undefined;
  }
  if (!options) {
    options = {};
  }

  return this._dropIndex({
    name: options.name,
    ignoreIfNotExists: options.ignoreIfNotExists
  }, callback);
};

/**
 * Retreives a list of the indexes currently configured on the cluster.
 *
 * @param callback
 *
 * @since 2.1.6
 * @uncommitted
 */
BucketManager.prototype.getIndexes = function(callback) {
  var qs = 'SELECT `indexes`.* FROM system:indexes';
  this._bucket.query(N1qlQuery.fromString(qs), function(err, rows) {
    if (err) {
      callback(err, null);
      return;
    }

    callback(null, rows);
  });
};

/**
 * Builds any indexes that were previously created with the deferred attribute.
 *
 * @param callback
 *
 * @since 2.1.6
 * @uncommitted
 */
BucketManager.prototype.buildDeferredIndexes = function(callback) {
  this.getIndexes(function(err, indexes) {
    if (err) {
      callback(err, null);
      return;
    }

    var deferredList = [];
    for (var i = 0; i < indexes.length; ++i) {
      if (indexes[i].state === 'deferred' || indexes[i].state === 'pending') {
        deferredList.push(indexes[i].name);
      }
    }

    if (deferredList.length === 0) {
      callback(null, deferredList);
      return;
    }

    var qs = '';
    qs += 'BUILD INDEX ON `' + this._bucket.name + '`('
    for (var j = 0; j < deferredList.length; ++i) {
      if (j > 0) {
        qs += ', ';
      }
      qs += '`' + deferredList[j] + '`';
    }

    this._bucket.query(N1qlQuery.fromString(qs), function(err) {
      if (err) {
        callback(err, deferredList);
        return;
      }

      callback(null, deferredList);
    });
  });
};

/**
 * Method for checking a list of indexes to see if they are active
 *
 * @param checkList
 * @param callback
 *
 * @private
 * @ignore
 */
BucketManager.prototype._checkIndexesActive = function(checkList, callback) {
  this.getIndexes(function(err, indexes) {
    if (err) {
      callback(err, false);
      return;
    }

    var checkIndexes = [];
    for (var i = 0; i < indexes.length; i++) {
      if (checkList.indexOf(indexes[i].name) !== -1) {
        checkIndexes.push(indexes[i]);
      }
    }

    if (checkIndexes.length != checkList.length) {
      callback(new Error('index not found'), false);
      return;
    }

    for (var j = 0; j < checkIndexes.length; ++j) {
      if (checkIndexes[j].state !== 'online') {
        callback(null, false);
        return;
      }
    }

    callback(null, true);
  });
};

/**
 * Watches a list of indexes; waiting for them to become available for use.
 *
 * @param watchList
 * @param options
 * @param options.timeout Timeout in millseconds
 * @param callback
 *
 * @since 2.1.6
 * @uncommitted
 */
BucketManager.prototype.watchIndexes = function(watchList, options, callback) {
  if (options instanceof Function) {
    callback = arguments[1];
    options = undefined;
  }
  if (!options) {
    options = {};
  }

  var timeoutTime = 0;
  if (options.timeout) {
    timeoutTime = Date.now() + options.timeout;
  }

  var self = this;
  var curInterval = 50;
  (function _checkIndexes() {
    self._checkIndexesActive(watchList, function(err, allActive) {
      if (err) {
        callback(err);
        return;
      }

      if (allActive) {
        callback(null);
        return;
      }

      if (timeoutTime > 0 && Date.now() + curInterval > timeoutTime) {
        callback(new Error('timeout'));
        return;
      }

      setTimeout(function() {
        curInterval += 500;
        if (curInterval > 1000) {
          curInterval = 1000;
        }

        _checkIndexes();
      }, curInterval);
    });
  })();
};

module.exports = BucketManager;
<|endoftext|>


<commit_before>(function(){

  angular
       .module('home')
       .controller('HomeController', [
          '$firebaseArray',
          'ngCountdownRibbon',
          HomeController
       ]);

  /**
   * Home Controller
   * @constructor
   */
  function HomeController($firebaseArray, ngCountdownRibbon) {

    // Initialize countdown ribbon
    var WEDDING_DAY = '2016-05-07',
        RIBBON_POSITION = 'right',
        RIBBON_YELLOW_THEME = 'yellow',
        RIBBON_YELLOW_CLASS = 'ribbon_yellow',
        RIBBON_HIDDEN_THEME = 'hidden',
        RIBBON_HIDDEN_CLASS = 'ribbon_hidden',
        todaysDate = new Date(),
        weddingDate = new Date(WEDDING_DAY),
        ribbonTheme;

    ngCountdownRibbon.addTheme(RIBBON_YELLOW_THEME,RIBBON_YELLOW_CLASS);
    ngCountdownRibbon.addTheme(RIBBON_HIDDEN_THEME,RIBBON_HIDDEN_CLASS);

    if (weddingDate > todaysDate){
      ribbonTheme = RIBBON_YELLOW_THEME;
    }
    else {
      ribbonTheme = RIBBON_HIDDEN_THEME;
    }

    ngCountdownRibbon.set(WEDDING_DAY, '', {position:RIBBON_POSITION,theme:ribbonTheme});

    // Initialize image gallery
    var picsRef = new Firebase(FIREBASE_URL+'gallerypics'),

      picsList = $firebaseArray(picsRef),

      self = this;

      picsList.$loaded()
        .then(function() {

          // Initialize pics with buffer
          var pics = [{}];

          picsList.forEach(function(p){
            var pic = {
              imageUrl: p.url
            };
            pics.push(pic);
          });

          // Add buffer to end of pics array
          pics.push({});

          self.pics = pics;

        });
  }

})();<commit_msg>Fixed small timezone bug in ribbon countdown timer<commit_after>(function(){

  angular
       .module('home')
       .controller('HomeController', [
          '$firebaseArray',
          'ngCountdownRibbon',
          HomeController
       ]);

  /**
   * Home Controller
   * @constructor
   */
  function HomeController($firebaseArray, ngCountdownRibbon) {

    // Initialize countdown ribbon
    var WEDDING_DAY = '2016-05-07',
        RIBBON_POSITION = 'right',
        RIBBON_YELLOW_THEME = 'yellow',
        RIBBON_YELLOW_CLASS = 'ribbon_yellow',
        RIBBON_HIDDEN_THEME = 'hidden',
        RIBBON_HIDDEN_CLASS = 'ribbon_hidden',
        todaysDate = new Date(),
        weddingDateUTC = new Date(WEDDING_DAY),
        weddingDate = new Date(weddingDateUTC.getTime() + weddingDateUTC.getTimezoneOffset()*60000),
        ribbonTheme;

    ngCountdownRibbon.addTheme(RIBBON_YELLOW_THEME,RIBBON_YELLOW_CLASS);
    ngCountdownRibbon.addTheme(RIBBON_HIDDEN_THEME,RIBBON_HIDDEN_CLASS);

    if (weddingDate > todaysDate){
      ribbonTheme = RIBBON_YELLOW_THEME;
    }
    else {
      ribbonTheme = RIBBON_HIDDEN_THEME;
    }

    ngCountdownRibbon.set(WEDDING_DAY, '', {position:RIBBON_POSITION,theme:ribbonTheme});

    // Initialize image gallery
    var picsRef = new Firebase(FIREBASE_URL+'gallerypics'),

      picsList = $firebaseArray(picsRef),

      self = this;

      picsList.$loaded()
        .then(function() {

          // Initialize pics with buffer
          var pics = [{}];

          picsList.forEach(function(p){
            var pic = {
              imageUrl: p.url
            };
            pics.push(pic);
          });

          // Add buffer to end of pics array
          pics.push({});

          self.pics = pics;

        });
  }

})();<|endoftext|>


<commit_before>var resolve = require('path').resolve;

// check whether an environment variable has been set
var env = process.env.NODE_ENV || 'development';

// environment settings
var cfg = require('./env/' + env);
cfg.env = env;
cfg.secret = require('./env/secret');

// express settings
cfg.pub_dir = resolve(__dirname, '../../public');
cfg.view_dir = resolve(__dirname, '../views');

module.exports = cfg;
<commit_msg>Twitter api key and secret are passed through environment variables now if secret.json does not exist (needed for heroku deploy)<commit_after>var resolve = require('path').resolve;

// check whether an environment variable has been set
var env = process.env.NODE_ENV || 'development';

// environment settings
var cfg = require('./env/' + env);
cfg.env = env;

cfg.secret = require('./env/secret') || { consumer_key: process.env.TW_KEY, consumer_secret: process.env.TW_SECRET };

// express settings
cfg.pub_dir = resolve(__dirname, '../../public');
cfg.view_dir = resolve(__dirname, '../views');

module.exports = cfg;
<|endoftext|>


<commit_before>import { expect } from 'chai';
import { createAction } from 'redux-actions';
import Immutable from 'seamless-immutable';

import types from 'constants/ActionTypes';
import Reservation from 'utils/fixtures/Reservation';
import Resource from 'utils/fixtures/Resource';
import User from 'utils/fixtures/User';
import dataReducer, { handleData } from './dataReducer';

describe('state/reducers/dataReducer', () => {
  describe('initial state', () => {
    const initialState = dataReducer(undefined, {});

    it('reservations is an empty object', () => {
      expect(initialState.reservations).to.deep.equal({});
    });

    it('resources is an empty object', () => {
      expect(initialState.resources).to.deep.equal({});
    });

    it('units is an empty object', () => {
      expect(initialState.units).to.deep.equal({});
    });

    it('users is an empty object', () => {
      expect(initialState.users).to.deep.equal({});
    });
  });

  describe('handling data', () => {
    const data = {
      resources: {
        'r-1': { value: 'some-value' },
      },
    };

    it('adds the given entities to state', () => {
      const initialState = Immutable({
        resources: {},
      });
      const expectedState = Immutable({
        resources: {
          'r-1': { value: 'some-value' },
        },
      });
      const nextState = handleData(initialState, data);

      expect(nextState).to.deep.equal(expectedState);
    });

    it('does not remove other entities in the same data collection', () => {
      const initialState = Immutable({
        resources: {
          'r-2': { value: 'other-value' },
        },
      });
      const expectedState = Immutable({
        resources: {
          'r-1': { value: 'some-value' },
          'r-2': { value: 'other-value' },
        },
      });
      const nextState = handleData(initialState, data);

      expect(nextState).to.deep.equal(expectedState);
    });

    it('overrides values with the same id in the same data collection', () => {
      const initialState = Immutable({
        resources: {
          'r-1': { value: 'override this' },
        },
      });
      const expectedState = Immutable({
        resources: {
          'r-1': { value: 'some-value' },
        },
      });
      const nextState = handleData(initialState, data);

      expect(nextState).to.deep.equal(expectedState);
    });

    it('does not change the other data collections', () => {
      const initialState = Immutable({
        resources: {},
        units: {
          'u-1': { value: 'unit-value' },
        },
      });
      const expectedState = Immutable({
        resources: {
          'r-1': { value: 'some-value' },
        },
        units: {
          'u-1': { value: 'unit-value' },
        },
      });
      const nextState = handleData(initialState, data);

      expect(nextState).to.deep.equal(expectedState);
      expect(nextState.units).to.equal(initialState.units);
    });
  });

  describe('handling actions', () => {
    describe('API.RESERVATION_POST_SUCCESS', () => {
      const postReservationSuccess = createAction(types.API.RESERVATION_POST_SUCCESS);

      it('adds the reservation to reservations', () => {
        const reservation = Reservation.build();
        const initialState = Immutable({
          reservations: {},
          resources: {},
        });
        const action = postReservationSuccess(reservation);
        const nextState = dataReducer(initialState, action);

        const actualReservations = nextState.reservations;
        const expectedReservations = Immutable({
          [reservation.url]: reservation,
        });

        expect(actualReservations).to.deep.equal(expectedReservations);
      });

      it('adds the given reservation to correct resource', () => {
        const resource = Resource.build();
        const reservation = Reservation.build({ resource: resource.id });
        const initialState = Immutable({
          reservations: {},
          resources: { [resource.id]: resource },
        });
        const action = postReservationSuccess(reservation);
        const nextState = dataReducer(initialState, action);
        const actualReservations = nextState.resources[resource.id].reservations;
        const expectedReservations = Immutable([reservation]);

        expect(actualReservations).to.deep.equal(expectedReservations);
      });

      it('does not touch other resource values', () => {
        const resource = Resource.build({
          otherValue: 'whatever',
        });
        const reservation = Reservation.build({ resource: resource.id });
        const initialState = Immutable({
          reservations: {},
          resources: { [resource.id]: resource },
        });
        const action = postReservationSuccess(reservation);
        const nextState = dataReducer(initialState, action);
        const expectedValue = resource.otherValue;
        const actualvalue = nextState.resources[resource.id].otherValue;

        expect(expectedValue).to.deep.equal(actualvalue);
      });
    });

    describe('API.RESERVATION_PUT_SUCCESS', () => {
      const putReservationSuccess = createAction(types.API.RESERVATION_PUT_SUCCESS);

      describe('updating reservations', () => {
        it('adds the reservation to reservations if it is not already there', () => {
          const reservation = Reservation.build();
          const initialState = Immutable({
            reservations: {},
            resources: {},
          });
          const action = putReservationSuccess(reservation);
          const nextState = dataReducer(initialState, action);

          const actualReservations = nextState.reservations;
          const expectedReservations = Immutable({
            [reservation.url]: reservation,
          });

          expect(actualReservations).to.deep.equal(expectedReservations);
        });

        it('updates a reservation already in reservations', () => {
          const oldReservation = Reservation.build({
            begin: 'old-begin',
            end: 'old-end',
          });
          const initialState = Immutable({
            reservations: { [oldReservation.url]: oldReservation },
            resources: {},
          });
          const updatedReservation = Reservation.build({
            begin: 'new-begin',
            end: 'new-end',
            url: oldReservation.url,
          });
          const action = putReservationSuccess(updatedReservation);
          const nextState = dataReducer(initialState, action);

          const actualReservations = nextState.reservations;
          const expectedReservations = Immutable({
            [updatedReservation.url]: updatedReservation,
          });

          expect(actualReservations).to.deep.equal(expectedReservations);
        });
      });

      describe('updating resource reservations', () => {
        it('adds the given reservation to correct resource', () => {
          const resource = Resource.build();
          const reservation = Reservation.build({ resource: resource.id });
          const initialState = Immutable({
            reservations: {},
            resources: { [resource.id]: resource },
          });
          const action = putReservationSuccess(reservation);
          const nextState = dataReducer(initialState, action);
          const actualReservations = nextState.resources[resource.id].reservations;
          const expectedReservations = Immutable([reservation]);

          expect(actualReservations).to.deep.equal(expectedReservations);
        });

        it('updates a reservation already in resource reservations', () => {
          const resource = Resource.build();
          const oldReservation = Reservation.build({
            begin: 'old-begin',
            end: 'old-end',
            resource: resource.id,
          });
          resource.reservations = [oldReservation];

          const initialState = Immutable({
            reservations: {},
            resources: { [resource.id]: resource },
          });
          const updatedReservation = Reservation.build({
            begin: 'new-begin',
            end: 'new-end',
            resource: resource.id,
            url: oldReservation.url,
          });
          const action = putReservationSuccess(updatedReservation);
          const nextState = dataReducer(initialState, action);

          const actualReservations = nextState.resources[resource.id].reservations;
          const expectedReservations = Immutable([updatedReservation]);

          expect(actualReservations).to.deep.equal(expectedReservations);
        });

        it('does not touch other resource values', () => {
          const resource = Resource.build({
            otherValue: 'whatever',
          });
          const reservation = Reservation.build({ resource: resource.id });
          const initialState = Immutable({
            reservations: {},
            resources: { [resource.id]: resource },
          });
          const action = putReservationSuccess(reservation);
          const nextState = dataReducer(initialState, action);
          const expectedValue = resource.otherValue;
          const actualvalue = nextState.resources[resource.id].otherValue;

          expect(expectedValue).to.deep.equal(actualvalue);
        });
      });
    });

    describe('API.RESERVATION_DELETE_SUCCESS', () => {
      const deleteReservationSuccess = createAction(types.API.RESERVATION_DELETE_SUCCESS);

      it('changes reservation state to cancelled in reservations', () => {
        const reservation = Reservation.build({ state: 'confirmed' });
        const initialState = Immutable({
          reservations: { [reservation.url]: reservation },
          resources: {},
        });
        const action = deleteReservationSuccess(reservation);
        const nextState = dataReducer(initialState, action);

        const actual = nextState.reservations[reservation.url];
        const expected = Object.assign({}, reservation, { state: 'cancelled' });

        expect(actual).to.deep.equal(expected);
      });

      it('changes reservation state to cancelled in resource reservations', () => {
        const resource = Resource.build();
        const reservation = Reservation.build({ resource: resource.id, state: 'confirmed' });
        resource.reservations = [reservation];

        const initialState = Immutable({
          reservations: {},
          resources: { [resource.id]: resource },
        });
        const action = deleteReservationSuccess(reservation);
        const nextState = dataReducer(initialState, action);

        const actualReservations = nextState.resources[resource.id].reservations;
        const expected = Immutable([Object.assign({}, reservation, { state: 'cancelled' })]);

        expect(actualReservations[0].state).to.deep.equal(expected[0].state);
      });

      it('does not touch other resource values', () => {
        const resource = Resource.build({
          otherValue: 'whatever',
        });
        const reservation = Reservation.build({ resource: resource.id, state: 'confirmed' });
        const initialState = Immutable({
          reservations: {},
          resources: { [resource.id]: resource },
        });
        const action = deleteReservationSuccess(reservation);
        const nextState = dataReducer(initialState, action);
        const expectedValue = resource.otherValue;
        const actualvalue = nextState.resources[resource.id].otherValue;

        expect(expectedValue).to.deep.equal(actualvalue);
      });
    });

    describe('API.RESOURCES_GET_SUCCESS', () => {
      const resourcesGetSuccess = createAction(
        types.API.RESOURCES_GET_SUCCESS,
        resource => ({ entities: { resources: { [resource.id]: resource } } })
      );

      it('adds resources to state', () => {
        const resource = Resource.build();
        const initialState = Immutable({
          resources: {},
        });
        const action = resourcesGetSuccess(resource);
        const nextState = dataReducer(initialState, action);

        const expected = Immutable({
          [resource.id]: resource,
        });

        expect(nextState.resources).to.deep.equal(expected);
      });

      it('removes resource.reservations if it is null', () => {
        const resource = { id: 'resource-1', reservations: null };
        const initialState = Immutable({
          resources: {},
        });
        const action = resourcesGetSuccess(resource);
        const nextState = dataReducer(initialState, action);

        const expected = Immutable({
          [resource.id]: { id: 'resource-1' },
        });

        expect(nextState.resources).to.deep.equal(expected);
      });

      it('does not replace old reservations value with null', () => {
        const originalResource = Resource.build({
          reservations: [{ foo: 'bar' }],
          state: 'requested',
        });
        const updatedResource = Resource.build({
          id: originalResource.id,
          reservations: null,
          state: 'confirmed',
        });
        const initialState = Immutable({
          resources: { [originalResource.id]: originalResource },
        });
        const action = resourcesGetSuccess(updatedResource);
        const nextState = dataReducer(initialState, action);
        const actualResource = nextState.resources[originalResource.id];

        expect(actualResource.reservations).to.deep.equal(originalResource.reservations);
        expect(actualResource.state).to.equal(updatedResource.state);
      });
    });

    describe('API.USER_GET_SUCCESS', () => {
      const userGetSuccess = createAction(types.API.USER_GET_SUCCESS);

      it('adds the user to users', () => {
        const user = User.build();
        const initialState = Immutable({
          users: {},
        });
        const action = userGetSuccess(user);
        const nextState = dataReducer(initialState, action);

        const actualUsers = nextState.users;
        const expectedUsers = Immutable({
          [user.uuid]: user,
        });

        expect(actualUsers).to.deep.equal(expectedUsers);
      });

      it('does not affect previous user values', () => {
        const user = User.build({ previousValue: 'value' });
        const updatedUser = User.build({ uuid: user.uuid, newValue: 'newValue' });
        const initialState = Immutable({
          users: { [user.uuid]: user },
        });
        const action = userGetSuccess(updatedUser);
        const nextState = dataReducer(initialState, action);
        const actualUser = nextState.users[user.uuid];

        expect(actualUser.previousValue).to.equal(user.previousValue);
        expect(actualUser.newValue).to.equal(updatedUser.newValue);
      });
    });

    describe('API.RESOURCE_UNFAVORITE_POST_SUCCESS', () => {
      const resource = {
        id: '123',
        isFavorite: true,
      };

      const unfavoriteResource = createAction(
        types.API.RESOURCE_UNFAVORITE_POST_SUCCESS,
        payload => payload,
        () => ({ id: resource.id })
      );

      it('changes isFavorite attribute from resource', () => {
        const user = User.build();
        const initialState = Immutable({
          resources: {
            [resource.id]: resource,
          },
        });
        const action = unfavoriteResource(user);
        const nextState = dataReducer(initialState, action);

        expect(nextState.resources[resource.id].isFavorite).to.be.false;
      });
    });

    describe('API.RESOURCE_FAVORITE_POST_SUCCESS', () => {
      const resource = {
        id: '123',
        isFavorite: false,
      };

      const unfavoriteResource = createAction(
        types.API.RESOURCE_FAVORITE_POST_SUCCESS,
        payload => payload,
        () => ({ id: resource.id })
      );

      it('changes isFavorite attribute from resource', () => {
        const user = User.build();
        const initialState = Immutable({
          resources: {
            [resource.id]: resource,
          },
        });
        const action = unfavoriteResource(user);
        const nextState = dataReducer(initialState, action);

        expect(nextState.resources[resource.id].isFavorite).to.be.true;
      });
    });
  });
});
<commit_msg>Add tests for SEARCH_RESULTS_GET_SUCCESS<commit_after>import { expect } from 'chai';
import { createAction } from 'redux-actions';
import Immutable from 'seamless-immutable';

import types from 'constants/ActionTypes';
import Reservation from 'utils/fixtures/Reservation';
import Resource from 'utils/fixtures/Resource';
import User from 'utils/fixtures/User';
import dataReducer, { handleData } from './dataReducer';

describe('state/reducers/dataReducer', () => {
  describe('initial state', () => {
    const initialState = dataReducer(undefined, {});

    it('reservations is an empty object', () => {
      expect(initialState.reservations).to.deep.equal({});
    });

    it('resources is an empty object', () => {
      expect(initialState.resources).to.deep.equal({});
    });

    it('units is an empty object', () => {
      expect(initialState.units).to.deep.equal({});
    });

    it('users is an empty object', () => {
      expect(initialState.users).to.deep.equal({});
    });
  });

  describe('handling data', () => {
    const data = {
      resources: {
        'r-1': { value: 'some-value' },
      },
    };

    it('adds the given entities to state', () => {
      const initialState = Immutable({
        resources: {},
      });
      const expectedState = Immutable({
        resources: {
          'r-1': { value: 'some-value' },
        },
      });
      const nextState = handleData(initialState, data);

      expect(nextState).to.deep.equal(expectedState);
    });

    it('does not remove other entities in the same data collection', () => {
      const initialState = Immutable({
        resources: {
          'r-2': { value: 'other-value' },
        },
      });
      const expectedState = Immutable({
        resources: {
          'r-1': { value: 'some-value' },
          'r-2': { value: 'other-value' },
        },
      });
      const nextState = handleData(initialState, data);

      expect(nextState).to.deep.equal(expectedState);
    });

    it('overrides values with the same id in the same data collection', () => {
      const initialState = Immutable({
        resources: {
          'r-1': { value: 'override this' },
        },
      });
      const expectedState = Immutable({
        resources: {
          'r-1': { value: 'some-value' },
        },
      });
      const nextState = handleData(initialState, data);

      expect(nextState).to.deep.equal(expectedState);
    });

    it('does not change the other data collections', () => {
      const initialState = Immutable({
        resources: {},
        units: {
          'u-1': { value: 'unit-value' },
        },
      });
      const expectedState = Immutable({
        resources: {
          'r-1': { value: 'some-value' },
        },
        units: {
          'u-1': { value: 'unit-value' },
        },
      });
      const nextState = handleData(initialState, data);

      expect(nextState).to.deep.equal(expectedState);
      expect(nextState.units).to.equal(initialState.units);
    });
  });

  describe('handling actions', () => {
    describe('API.RESERVATION_POST_SUCCESS', () => {
      const postReservationSuccess = createAction(types.API.RESERVATION_POST_SUCCESS);

      it('adds the reservation to reservations', () => {
        const reservation = Reservation.build();
        const initialState = Immutable({
          reservations: {},
          resources: {},
        });
        const action = postReservationSuccess(reservation);
        const nextState = dataReducer(initialState, action);

        const actualReservations = nextState.reservations;
        const expectedReservations = Immutable({
          [reservation.url]: reservation,
        });

        expect(actualReservations).to.deep.equal(expectedReservations);
      });

      it('adds the given reservation to correct resource', () => {
        const resource = Resource.build();
        const reservation = Reservation.build({ resource: resource.id });
        const initialState = Immutable({
          reservations: {},
          resources: { [resource.id]: resource },
        });
        const action = postReservationSuccess(reservation);
        const nextState = dataReducer(initialState, action);
        const actualReservations = nextState.resources[resource.id].reservations;
        const expectedReservations = Immutable([reservation]);

        expect(actualReservations).to.deep.equal(expectedReservations);
      });

      it('does not touch other resource values', () => {
        const resource = Resource.build({
          otherValue: 'whatever',
        });
        const reservation = Reservation.build({ resource: resource.id });
        const initialState = Immutable({
          reservations: {},
          resources: { [resource.id]: resource },
        });
        const action = postReservationSuccess(reservation);
        const nextState = dataReducer(initialState, action);
        const expectedValue = resource.otherValue;
        const actualvalue = nextState.resources[resource.id].otherValue;

        expect(expectedValue).to.deep.equal(actualvalue);
      });
    });

    describe('API.RESERVATION_PUT_SUCCESS', () => {
      const putReservationSuccess = createAction(types.API.RESERVATION_PUT_SUCCESS);

      describe('updating reservations', () => {
        it('adds the reservation to reservations if it is not already there', () => {
          const reservation = Reservation.build();
          const initialState = Immutable({
            reservations: {},
            resources: {},
          });
          const action = putReservationSuccess(reservation);
          const nextState = dataReducer(initialState, action);

          const actualReservations = nextState.reservations;
          const expectedReservations = Immutable({
            [reservation.url]: reservation,
          });

          expect(actualReservations).to.deep.equal(expectedReservations);
        });

        it('updates a reservation already in reservations', () => {
          const oldReservation = Reservation.build({
            begin: 'old-begin',
            end: 'old-end',
          });
          const initialState = Immutable({
            reservations: { [oldReservation.url]: oldReservation },
            resources: {},
          });
          const updatedReservation = Reservation.build({
            begin: 'new-begin',
            end: 'new-end',
            url: oldReservation.url,
          });
          const action = putReservationSuccess(updatedReservation);
          const nextState = dataReducer(initialState, action);

          const actualReservations = nextState.reservations;
          const expectedReservations = Immutable({
            [updatedReservation.url]: updatedReservation,
          });

          expect(actualReservations).to.deep.equal(expectedReservations);
        });
      });

      describe('updating resource reservations', () => {
        it('adds the given reservation to correct resource', () => {
          const resource = Resource.build();
          const reservation = Reservation.build({ resource: resource.id });
          const initialState = Immutable({
            reservations: {},
            resources: { [resource.id]: resource },
          });
          const action = putReservationSuccess(reservation);
          const nextState = dataReducer(initialState, action);
          const actualReservations = nextState.resources[resource.id].reservations;
          const expectedReservations = Immutable([reservation]);

          expect(actualReservations).to.deep.equal(expectedReservations);
        });

        it('updates a reservation already in resource reservations', () => {
          const resource = Resource.build();
          const oldReservation = Reservation.build({
            begin: 'old-begin',
            end: 'old-end',
            resource: resource.id,
          });
          resource.reservations = [oldReservation];

          const initialState = Immutable({
            reservations: {},
            resources: { [resource.id]: resource },
          });
          const updatedReservation = Reservation.build({
            begin: 'new-begin',
            end: 'new-end',
            resource: resource.id,
            url: oldReservation.url,
          });
          const action = putReservationSuccess(updatedReservation);
          const nextState = dataReducer(initialState, action);

          const actualReservations = nextState.resources[resource.id].reservations;
          const expectedReservations = Immutable([updatedReservation]);

          expect(actualReservations).to.deep.equal(expectedReservations);
        });

        it('does not touch other resource values', () => {
          const resource = Resource.build({
            otherValue: 'whatever',
          });
          const reservation = Reservation.build({ resource: resource.id });
          const initialState = Immutable({
            reservations: {},
            resources: { [resource.id]: resource },
          });
          const action = putReservationSuccess(reservation);
          const nextState = dataReducer(initialState, action);
          const expectedValue = resource.otherValue;
          const actualvalue = nextState.resources[resource.id].otherValue;

          expect(expectedValue).to.deep.equal(actualvalue);
        });
      });
    });

    describe('API.RESERVATION_DELETE_SUCCESS', () => {
      const deleteReservationSuccess = createAction(types.API.RESERVATION_DELETE_SUCCESS);

      it('changes reservation state to cancelled in reservations', () => {
        const reservation = Reservation.build({ state: 'confirmed' });
        const initialState = Immutable({
          reservations: { [reservation.url]: reservation },
          resources: {},
        });
        const action = deleteReservationSuccess(reservation);
        const nextState = dataReducer(initialState, action);

        const actual = nextState.reservations[reservation.url];
        const expected = Object.assign({}, reservation, { state: 'cancelled' });

        expect(actual).to.deep.equal(expected);
      });

      it('changes reservation state to cancelled in resource reservations', () => {
        const resource = Resource.build();
        const reservation = Reservation.build({ resource: resource.id, state: 'confirmed' });
        resource.reservations = [reservation];

        const initialState = Immutable({
          reservations: {},
          resources: { [resource.id]: resource },
        });
        const action = deleteReservationSuccess(reservation);
        const nextState = dataReducer(initialState, action);

        const actualReservations = nextState.resources[resource.id].reservations;
        const expected = Immutable([Object.assign({}, reservation, { state: 'cancelled' })]);

        expect(actualReservations[0].state).to.deep.equal(expected[0].state);
      });

      it('does not touch other resource values', () => {
        const resource = Resource.build({
          otherValue: 'whatever',
        });
        const reservation = Reservation.build({ resource: resource.id, state: 'confirmed' });
        const initialState = Immutable({
          reservations: {},
          resources: { [resource.id]: resource },
        });
        const action = deleteReservationSuccess(reservation);
        const nextState = dataReducer(initialState, action);
        const expectedValue = resource.otherValue;
        const actualvalue = nextState.resources[resource.id].otherValue;

        expect(expectedValue).to.deep.equal(actualvalue);
      });
    });

    describe('API.RESOURCES_GET_SUCCESS', () => {
      const resourcesGetSuccess = createAction(
        types.API.RESOURCES_GET_SUCCESS,
        resource => ({ entities: { resources: { [resource.id]: resource } } })
      );

      it('adds resources to state', () => {
        const resource = Resource.build();
        const initialState = Immutable({
          resources: {},
        });
        const action = resourcesGetSuccess(resource);
        const nextState = dataReducer(initialState, action);

        const expected = Immutable({
          [resource.id]: resource,
        });

        expect(nextState.resources).to.deep.equal(expected);
      });

      it('removes resource.reservations if it is null', () => {
        const resource = { id: 'resource-1', reservations: null };
        const initialState = Immutable({
          resources: {},
        });
        const action = resourcesGetSuccess(resource);
        const nextState = dataReducer(initialState, action);

        const expected = Immutable({
          [resource.id]: { id: 'resource-1' },
        });

        expect(nextState.resources).to.deep.equal(expected);
      });

      it('does not replace old reservations value with null', () => {
        const originalResource = Resource.build({
          reservations: [{ foo: 'bar' }],
          state: 'requested',
        });
        const updatedResource = Resource.build({
          id: originalResource.id,
          reservations: null,
          state: 'confirmed',
        });
        const initialState = Immutable({
          resources: { [originalResource.id]: originalResource },
        });
        const action = resourcesGetSuccess(updatedResource);
        const nextState = dataReducer(initialState, action);
        const actualResource = nextState.resources[originalResource.id];

        expect(actualResource.reservations).to.deep.equal(originalResource.reservations);
        expect(actualResource.state).to.equal(updatedResource.state);
      });
    });

    describe('API.SEARCH_RESULTS_GET_SUCCESS', () => {
      const searchResultsGetSuccess = createAction(
        types.API.SEARCH_RESULTS_GET_SUCCESS,
        resource => ({ entities: { resources: { [resource.id]: resource } } })
      );

      it('adds resources to state', () => {
        const resource = Resource.build();
        const initialState = Immutable({
          resources: {},
        });
        const action = searchResultsGetSuccess(resource);
        const nextState = dataReducer(initialState, action);

        const expected = Immutable({
          [resource.id]: resource,
        });

        expect(nextState.resources).to.deep.equal(expected);
      });

      it('does not keep old fields in resource', () => {
        const originalResource = Resource.build({
          distance: 400000,
          state: 'requested',
        });
        const updatedResource = Resource.build({
          id: originalResource.id,
          state: 'confirmed',
        });
        const initialState = Immutable({
          resources: { [originalResource.id]: originalResource },
        });
        const action = searchResultsGetSuccess(updatedResource);
        const nextState = dataReducer(initialState, action);
        const actualResource = nextState.resources[originalResource.id];

        expect(actualResource).to.deep.equal(updatedResource);
      });
    });

    describe('API.USER_GET_SUCCESS', () => {
      const userGetSuccess = createAction(types.API.USER_GET_SUCCESS);

      it('adds the user to users', () => {
        const user = User.build();
        const initialState = Immutable({
          users: {},
        });
        const action = userGetSuccess(user);
        const nextState = dataReducer(initialState, action);

        const actualUsers = nextState.users;
        const expectedUsers = Immutable({
          [user.uuid]: user,
        });

        expect(actualUsers).to.deep.equal(expectedUsers);
      });

      it('does not affect previous user values', () => {
        const user = User.build({ previousValue: 'value' });
        const updatedUser = User.build({ uuid: user.uuid, newValue: 'newValue' });
        const initialState = Immutable({
          users: { [user.uuid]: user },
        });
        const action = userGetSuccess(updatedUser);
        const nextState = dataReducer(initialState, action);
        const actualUser = nextState.users[user.uuid];

        expect(actualUser.previousValue).to.equal(user.previousValue);
        expect(actualUser.newValue).to.equal(updatedUser.newValue);
      });
    });

    describe('API.RESOURCE_UNFAVORITE_POST_SUCCESS', () => {
      const resource = {
        id: '123',
        isFavorite: true,
      };

      const unfavoriteResource = createAction(
        types.API.RESOURCE_UNFAVORITE_POST_SUCCESS,
        payload => payload,
        () => ({ id: resource.id })
      );

      it('changes isFavorite attribute from resource', () => {
        const user = User.build();
        const initialState = Immutable({
          resources: {
            [resource.id]: resource,
          },
        });
        const action = unfavoriteResource(user);
        const nextState = dataReducer(initialState, action);

        expect(nextState.resources[resource.id].isFavorite).to.be.false;
      });
    });

    describe('API.RESOURCE_FAVORITE_POST_SUCCESS', () => {
      const resource = {
        id: '123',
        isFavorite: false,
      };

      const unfavoriteResource = createAction(
        types.API.RESOURCE_FAVORITE_POST_SUCCESS,
        payload => payload,
        () => ({ id: resource.id })
      );

      it('changes isFavorite attribute from resource', () => {
        const user = User.build();
        const initialState = Immutable({
          resources: {
            [resource.id]: resource,
          },
        });
        const action = unfavoriteResource(user);
        const nextState = dataReducer(initialState, action);

        expect(nextState.resources[resource.id].isFavorite).to.be.true;
      });
    });
  });
});
<|endoftext|>


<commit_before>'use strict';
var path = require('path');
var child_process = require('child_process');

/**
 * @constructor
 */
function ChangeLog(opt_options) {
  opt_options = opt_options || {};

  this.cwd = path.resolve(process.cwd(), opt_options.cwd || '');

  this.since = opt_options.since || '--tags';

  this.commitURL = opt_options.commitURL || '<commit-url>';
  this.pullRequestURL = opt_options.pullRequestURL || '<pr-url>';

  this.group = opt_options.group || false;
  this.author = opt_options.author || false;
  this.link = opt_options.link || false;
  this.merge = opt_options.merge || false;

  this._scopeRE = /^([a-zA-Z0-9]+[a-zA-Z0-9\/\._-]*)(?:,\s[a-zA-Z0-9]+[a-zA-Z0-9\/\._-]*)*:/;
  this._format = '{"hash":"%H","subject":"%s","author":{"name":"%an","email":"%ae"},"body":"%b"}';
}
module.exports = ChangeLog;

ChangeLog.prototype.getDate = function(commitish, callback) {
  var cmd = 'git show -s --format=%ad `git rev-list --max-count=1 ' + commitish + '`';

  child_process.exec(cmd, {
    cwd: this.cwd
  }, function(err, stdout) {
    if (err) {
      return callback(err);
    }
    var date = stdout.trim();

    callback(null, date);
  });
};

ChangeLog.prototype.getLog = function(since, callback) {
  var self = this;
  var cmd = 'git log --format=\'' + this._format + '\'';

  if (since) {
    cmd += ' --since="' + since + '"';
  }

  child_process.exec(cmd, {
    cwd: this.cwd
  }, function(err, stdout) {
    if (err) {
      return callback(err);
    }

    var i, commit, subject, matches;
    var commits = [];
    var raw = stdout
        .substr(1)
        .trim()
        .replace(/\n/g, '\\n')
        .split('}\\n{');

    raw.pop(); // remove limiting commit AND closing curly

    for (i = 0; i < raw.length; ++i) {
      raw[i] = '{' + raw[i] + '}';

      commit = JSON.parse(raw[i]);
      subject = commit.subject;

      if (subject.toLowerCase().indexOf('working on') > -1 ||
          !self.merge && subject.substr(0, 5) === 'Merge') {
        continue;
      }

      // detect scopes
      matches = subject.match(self._scopeRE);

      if (matches) {
        commit.scope = subject.substr(0, matches[0].length - 1);
        commit.subject = subject.substr(matches[0].length).trim();
      }

      // detect semver tags
      if (commit.body && commit.body.indexOf('SEMVER-MAJOR') > -1) {
        commit.semver = 'SEMVER-MAJOR';

      } else if (commit.body && commit.body.indexOf('SEMVER-MINOR') > -1) {
        commit.semver = 'SEMVER-MINOR';
      }

      commits.push(commit);
    }

    callback(null, commits);
  });
};

ChangeLog.prototype.toMarkdown = function(commits) {
  var i, commit, md;
  var markdown = [];

  if (this.group) {
    commits.sort(function(a, b) {
      return a.scope > b.scope;
    });
  }

  for (i = 0; i < commits.length; ++i) {
    commit = commits[i];

    if (this.link) {
      md = '[[`' + commit.hash.substr(0, 10) + '`](' +
          this.commitURL + '/' + commit.hash + ')] - ';
    } else {
      md = '[`' + commit.hash.substr(0, 10) + '`] - ';
    }

    if (commit.scope || commit.semver) {
      md += '**';

      if (commit.semver) {
        md += '(' + commit.semver + ')';

        if (commit.scope) {
          md += ' ';
        }
      }
      if (commit.scope) {
        md += commit.scope;
      }

      md += '**';

      if (commit.scope) {
        md += ': ';
      }
    }

    md += commit.subject;

    if (this.author) {
      md += ' (' + commit.author.name + ')';
    }

    markdown.push(md);
  }

  return markdown;
};
<commit_msg>ChangeLog: fix scope sorting<commit_after>'use strict';
var path = require('path');
var child_process = require('child_process');

/**
 * @constructor
 */
function ChangeLog(opt_options) {
  opt_options = opt_options || {};

  this.cwd = path.resolve(process.cwd(), opt_options.cwd || '');

  this.since = opt_options.since || '--tags';

  this.commitURL = opt_options.commitURL || '<commit-url>';
  this.pullRequestURL = opt_options.pullRequestURL || '<pr-url>';

  this.group = opt_options.group || false;
  this.author = opt_options.author || false;
  this.link = opt_options.link || false;
  this.merge = opt_options.merge || false;

  this._scopeRE = /^([a-zA-Z0-9]+[a-zA-Z0-9\/\._-]*)(?:,\s[a-zA-Z0-9]+[a-zA-Z0-9\/\._-]*)*:/;
  this._format = '{"hash":"%H","subject":"%s","author":{"name":"%an","email":"%ae"},"body":"%b"}';
}
module.exports = ChangeLog;

ChangeLog.prototype.getDate = function(commitish, callback) {
  var cmd = 'git show -s --format=%ad `git rev-list --max-count=1 ' + commitish + '`';

  child_process.exec(cmd, {
    cwd: this.cwd
  }, function(err, stdout) {
    if (err) {
      return callback(err);
    }
    var date = stdout.trim();

    callback(null, date);
  });
};

ChangeLog.prototype.getLog = function(since, callback) {
  var self = this;
  var cmd = 'git log --format=\'' + this._format + '\'';

  if (since) {
    cmd += ' --since="' + since + '"';
  }

  child_process.exec(cmd, {
    cwd: this.cwd
  }, function(err, stdout) {
    if (err) {
      return callback(err);
    }

    var i, commit, subject, matches;
    var commits = [];
    var raw = stdout
        .substr(1)
        .trim()
        .replace(/\n/g, '\\n')
        .split('}\\n{');

    raw.pop(); // remove limiting commit AND closing curly

    for (i = 0; i < raw.length; ++i) {
      raw[i] = '{' + raw[i] + '}';

      commit = JSON.parse(raw[i]);
      subject = commit.subject;

      if (subject.toLowerCase().indexOf('working on') > -1 ||
          !self.merge && subject.substr(0, 5) === 'Merge') {
        continue;
      }

      // detect scopes
      matches = subject.match(self._scopeRE);

      if (matches) {
        commit.scope = subject.substr(0, matches[0].length - 1);
        commit.subject = subject.substr(matches[0].length).trim();
      }

      // detect semver tags
      if (commit.body && commit.body.indexOf('SEMVER-MAJOR') > -1) {
        commit.semver = 'SEMVER-MAJOR';

      } else if (commit.body && commit.body.indexOf('SEMVER-MINOR') > -1) {
        commit.semver = 'SEMVER-MINOR';
      }

      commits.push(commit);
    }

    callback(null, commits);
  });
};

ChangeLog.prototype.toMarkdown = function(commits) {
  var i, commit, md;
  var markdown = [];

  if (this.group) {
    commits.sort(function(a, b) {
      if (a.scope > b.scope) {
        return 1;
      } else if (a.scope < b.scope) {
        return -1;
      }
      return 0;
    });
  }

  for (i = 0; i < commits.length; ++i) {
    commit = commits[i];

    if (this.link) {
      md = '[[`' + commit.hash.substr(0, 10) + '`](' +
          this.commitURL + '/' + commit.hash + ')] - ';
    } else {
      md = '[`' + commit.hash.substr(0, 10) + '`] - ';
    }

    if (commit.scope || commit.semver) {
      md += '**';

      if (commit.semver) {
        md += '(' + commit.semver + ')';

        if (commit.scope) {
          md += ' ';
        }
      }
      if (commit.scope) {
        md += commit.scope;
      }

      md += '**';

      if (commit.scope) {
        md += ': ';
      }
    }

    md += commit.subject;

    if (this.author) {
      md += ' (' + commit.author.name + ')';
    }

    markdown.push(md);
  }

  return markdown;
};
<|endoftext|>


<commit_before>'use strict';

const CWD = process.cwd();
const exec = require('child_process').exec;
const execon = require('execon');
const rendy = require('rendy');
const currify = require('currify');
const tryRequire = require('tryrequire');
const shortdate = require('shortdate');
const readPackage = require('read-pkg-up');
const gitTempl = 'git log {{ version }}..HEAD --pretty=format:"- %s" --grep {{ category }}| sed "s/\\b{{ category }}\\b//g"';

const onError = currify(_onError);
const changelog = currify(_changelog);
const getPkg = (data) => data.pkg;

module.exports = (versionNew, callback) => {
    readPackage()
        .then(getPkg)
        .then(changelog(versionNew))
        .catch(onError(callback));
};

function _onError(callback, error) {
    callback(Error(`error reading package.json: ${error.message}`));
}

function _changelog(versionNew, info) {
    const isV = /^v/.test(versionNew);
    const version = 'v' + info.version;

    const gitFix = rendy(gitTempl, {
       category: 'fix' ,
       version: version
    });

    const gitFeature = rendy(gitTempl, {
       category: 'feature',
       version: version
    });

    if (!isV && versionNew)
        versionNew  = 'v' + versionNew;

    execon.parallel([
        execon.with(exec, gitFix),
        execon.with(exec, gitFeature),
        ], (error, fixData, featureData) => {
            const DATA = 0;
            const STD_ERR = 1;
            const stderr = fixData[STD_ERR] || featureData[STD_ERR];

            let head = shortdate();
            let data = '';

            if (versionNew)
                head += ', ' + versionNew;

            head += '\n\n';

            if (!error) {
                fix = fixData[DATA];
                feature = featureData[DATA];
            }

            if (fix || feature) {
                data = head;

                if (fix) {
                    data += 'fix:'       + '\n';
                    data += fix          + '\n\n';
                }

                if (feature) {
                    data += 'feature:'   + '\n';
                    data += feature      + '\n\n';
                }
            }

            if (error)
                return callback(error);

            if (stderr)
                return callback(Error(stderr));

            if (!data)
                return callback(Error('No new feature and fix commits from ' + version));

            callback(null, data);
        });
};

<commit_msg>feature(changelog) add early return when error<commit_after>'use strict';

const CWD = process.cwd();
const exec = require('child_process').exec;
const execon = require('execon');
const rendy = require('rendy');
const currify = require('currify');
const tryRequire = require('tryrequire');
const shortdate = require('shortdate');
const readPackage = require('read-pkg-up');
const gitTempl = 'git log {{ version }}..HEAD --pretty=format:"- %s" --grep {{ category }}| sed "s/\\b{{ category }}\\b//g"';

const onError = currify(_onError);
const changelog = currify(_changelog);
const getPkg = (data) => data.pkg;

module.exports = (versionNew, callback) => {
    readPackage()
        .then(getPkg)
        .then(changelog(versionNew))
        .catch(onError(callback));
};

function _onError(callback, error) {
    callback(Error(`error reading package.json: ${error.message}`));
}

function _changelog(versionNew, info) {
    const isV = /^v/.test(versionNew);
    const version = 'v' + info.version;

    const gitFix = rendy(gitTempl, {
       category: 'fix' ,
       version: version
    });

    const gitFeature = rendy(gitTempl, {
       category: 'feature',
       version: version
    });

    if (!isV && versionNew)
        versionNew  = 'v' + versionNew;

    execon.parallel([
        execon.with(exec, gitFix),
        execon.with(exec, gitFeature),
        ], (error, fixData, featureData) => {
            if (error)
                return callback(error);

            const DATA = 0;
            const STD_ERR = 1;
            const stderr = fixData[STD_ERR] || featureData[STD_ERR];

            let head = shortdate();
            let data = '';

            if (versionNew)
                head += ', ' + versionNew;

            head += '\n\n';

            const fix = fixData[DATA];
            const feature = featureData[DATA];

            if (fix || feature) {
                data = head;

                if (fix) {
                    data += 'fix:'       + '\n';
                    data += fix          + '\n\n';
                }

                if (feature) {
                    data += 'feature:'   + '\n';
                    data += feature      + '\n\n';
                }
            }

            if (stderr)
                return callback(Error(stderr));

            if (!data)
                return callback(Error('No new feature and fix commits from ' + version));

            callback(null, data);
        });
};

<|endoftext|>


<commit_before>(function() {
    'use strict';

    var CWD         = process.cwd(),

        fs          = require('fs'),
        exec        = require('child_process').exec,

        Util        = require('util-io'),

        tryRequire  = require('tryrequire');

    module.exports = function(versionNew, callback) {
        var gitFix, gitFeature, version,
            info        = tryRequire(CWD + '/package'),
            isV         = /^v/.test(versionNew),

            name        = 'ChangeLog',

            gitTempl    = 'git log {{ version }}..HEAD --pretty=format:"- %s" --grep {{ category }} | sed  \'s/{{ category }}//g\'';

        if (!info) {
            callback({
                message: 'error reading package.json'
            });
        } else {
            version     = 'v' + info.version,

            gitFix      = Util.render(gitTempl, {
               category : 'fix' ,
               version  : version
            });

            gitFeature  = Util.render(gitTempl, {
               category: 'feature',
               version  : version
            });

            if (!isV && versionNew)
                versionNew  = 'v' + versionNew;

            fs.readFile(name, 'utf8', function(error, fileData) {
                Util.exec.parallel([
                    Util.exec.with(exec, gitFix),
                    Util.exec.with(exec, gitFeature),
                    ], function(error, fixData, featureData) {
                        var fix, feature,
                            DATA        = 0,
                            STD_ERR     = 1,
                            date        = Util.getShortDate(),
                            head        = date,
                            data        = '';

                        if (versionNew)
                            head       += ', ' + versionNew;

                        head            += '\n\n';

                        if (!error) {
                            fix         = fixData[DATA];
                            feature     = featureData[DATA];
                        }

                        if (fix || feature) {
                            data        = head;

                            if (fix) {
                                data    += 'fix:'       + '\n';
                                data    += fix          + '\n\n';
                            }

                            if (feature) {
                                data    += 'feature:'   + '\n';
                                data    += feature      + '\n\n';
                            }

                            if (fileData && versionNew)
                                data        += '\n' + fileData;
                        }

                        error   = error || fixData[STD_ERR] || featureData[STD_ERR];

                        if (error)
                            callback(error);
                        else if (!data)
                            console.log('No new feature and fix commits from', version);
                        else if (!versionNew)
                            console.log(data);
                        else
                            fs.writeFile(name, data, function(error) {
                                var msg = 'changelog: done';

                                callback(error, msg);
                            });
                    });
            });
        }
    };

})();
<commit_msg>fix(changelog) stderr -> Error(stderr)<commit_after>(function() {
    'use strict';

    var CWD         = process.cwd(),

        fs          = require('fs'),
        exec        = require('child_process').exec,

        Util        = require('util-io'),

        tryRequire  = require('tryrequire');

    module.exports = function(versionNew, callback) {
        var gitFix, gitFeature, version,
            info        = tryRequire(CWD + '/package'),
            isV         = /^v/.test(versionNew),

            name        = 'ChangeLog',

            gitTempl    = 'git log {{ version }}..HEAD --pretty=format:"- %s" --grep {{ category }} | sed  \'s/{{ category }}//g\'';

        if (!info) {
            callback({
                message: 'error reading package.json'
            });
        } else {
            version     = 'v' + info.version,

            gitFix      = Util.render(gitTempl, {
               category : 'fix' ,
               version  : version
            });

            gitFeature  = Util.render(gitTempl, {
               category: 'feature',
               version  : version
            });

            if (!isV && versionNew)
                versionNew  = 'v' + versionNew;

            fs.readFile(name, 'utf8', function(error, fileData) {
                Util.exec.parallel([
                    Util.exec.with(exec, gitFix),
                    Util.exec.with(exec, gitFeature),
                    ], function(error, fixData, featureData) {
                        var fix, feature,
                            DATA        = 0,
                            STD_ERR     = 1,
                            date        = Util.getShortDate(),
                            head        = date,
                            data        = '';

                        if (versionNew)
                            head       += ', ' + versionNew;

                        head            += '\n\n';

                        if (!error) {
                            fix         = fixData[DATA];
                            feature     = featureData[DATA];
                        }

                        if (fix || feature) {
                            data        = head;

                            if (fix) {
                                data    += 'fix:'       + '\n';
                                data    += fix          + '\n\n';
                            }

                            if (feature) {
                                data    += 'feature:'   + '\n';
                                data    += feature      + '\n\n';
                            }

                            if (fileData && versionNew)
                                data        += '\n' + fileData;
                        }

                        error   =   error                       ||
                                    Error(fixData[STD_ERR])     ||
                                    Error(featureData[STD_ERR]);

                        if (error)
                            callback(error);
                        else if (!data)
                            console.log('No new feature and fix commits from', version);
                        else if (!versionNew)
                            console.log(data);
                        else
                            fs.writeFile(name, data, function(error) {
                                var msg = 'changelog: done';

                                callback(error, msg);
                            });
                    });
            });
        }
    };

})();
<|endoftext|>


<commit_before>'use strict';

/**
 * class Charlatan
 *
 **/

var COMPILED_LOCALES_PATH = '../.locales/';

exports.Name        = require('./charlatan/name');
exports.Address     = require('./charlatan/address');
exports.PhoneNumber = require('./charlatan/phone_number');
exports.Internet    = require('./charlatan/internet');
exports.Company     = require('./charlatan/company');
exports.Lorem       = require('./charlatan/lorem');
var Helpers         = exports.Helpers = require('./charlatan/helpers');

require('js-yaml');
var Fs = require('fs');
var Path = require('path');

// locale files {<<locale_name>>: <<path_locale_file>>}
var avaliable_locales = {};

// already loaded locales {<<locale_name>>: <<locale_data>>}
var locales = {};

var current_locale;
var base_locale = 'en';

// getValue(data, path) -> mixed
// - data (object): locale
// - path (array): path to property
//
// get value from locale hash
function getValue(data, path) {
  if (!data[path[0]] ||
      (path.length > 0 && typeof(data[path[0]]) !== 'object')) {
    return undefined;
  }
  if (path.length === 1) {
    return data[path[0]];
  }
  return getValue(data[path[0]], path.slice(1));
}

// snake2camelcase(str) -> string
// - str (string): source string
//
// locale files use snake notation `xxx_yyy_zzz`
// but real methods use camelcase `xxxYyyZzz`
function snake2camelcase(str) {
  var parts = str.split('_');
  if (parts.length === 1) {
    return str;
  }
  return parts[0] + parts.slice(1).map(function (word) {
    return word.charAt(0).toUpperCase() + word.slice(1);
  }).join('');
}

/**
 * Charlatan.addLocale(name, data) -> void
 * - name (string): locale name, for example `en-us`
 * - data (object|string): locale hash or yaml file
 *
 * Load locale to Charlatan. Charlatan preload some
 * [locales](https://github.com/nodeca/charlatan/tree/master/lib)
 **/
var addLocale = exports.addLocale = function (name, data) {
  // if data not set, then check avaliable
  if (data === undefined) {
    if (!avaliable_locales[name]) {
      throw new Error('Locale not found:' + name);
    }

    data = avaliable_locales[name];

    // load base language, if exists
    var lang = name.split('-')[0];
    if (name !== lang && !!avaliable_locales[lang]) {
      addLocale(lang);
    }
  }
  // data in file
  if (typeof data === 'string') {
    if (!Fs.existsSync(data)) {
      throw new Error('Config ' + data + ' not found');
    }
    data = require(data)[name];
  }
  locales[name] = data;
  return true;
};

/**
 * Charlatan.setLocale(name) -> void
 * - name (string): locale name, for example `en-us`
 *
 *
 **/
exports.setLocale = function (name) {
  if (!locales[name]) {
    addLocale(name);
  }
  current_locale = name;
  return true;
};

/**
 * Charlatan.translate(key) -> mixed
 * - key (string): key in locale, for example `address.city_prefix`
 *
 * Returns a phrase from the locale.
 *
 * Fallbacks to base language (and then to `en`)
 *   if it was not found: `ru_RU -> ru -> en`
 **/
exports.translate = function (key) {
  var lang = current_locale.split('-')[0];
  var path = key.split('.');
  var result = getValue(locales[current_locale], path);

  // fallback to lang
  if (result === undefined && lang !== current_locale) {
    result = getValue(locales[lang], path);
  }

  // fallback to base_lang
  if (result === undefined) {
    result = getValue(locales[base_locale], path);
  }

  // oops
  if (result === undefined) {
    throw new Error('Invalid path:' + key);
  }

  return result;
};

/**
 * Charlatan.fetch(key) -> mixed
 * - key (string): key in locale, for example `address.city_prefix`
 *
 * Helper for the common approach of grabbing a translation
 * with an array of values and selecting one of them.
 **/
var fetch = exports.fetch = function (key) {
  var fetched = exports.translate(key);
  if (fetched.length !== undefined) {
    fetched = Helpers.sample(fetched);
  }
  // need regexfy from ruby version?
  return fetched;
};

/**
 * Charlatan.parse(key) -> string
 * - key (string): key in locale, for example `address.city_prefix`
 *
 * Load formatted strings from the locale, "parsing" them
 * into method calls that can be used to generate a
 * formatted translation: e.g., "#{first_name} #{last_name}".
 **/
exports.parse = function (key) {
  var match, kls, method, etc;
  var re = /#\{([A-Za-z]+(?:\.))?([^\}]+)\}([^#]+)?/g;
  var str = fetch(key);
  var text = '';

  match = re.exec(str);
  if (!match) {
    return str;
  }
  do {
    kls = match[1];
    method = match[2];
    etc = match[3];
    // If the token had a class Prefix (e.g., Name.first_name)
    // take it, otherwise use base namespace part
    if (!kls) {
      kls = exports.Helpers.capitalize(key.split('.')[0]);
    }
    else {
      kls = kls.slice(0, -1);
    }
    // If the class has the method, call it, otherwise
    // fetch the transation
    if (!!exports[kls] &&
        typeof(exports[kls][snake2camelcase(method)]) === 'function') {

      text += exports[kls][snake2camelcase(method)].call(exports[kls]);
    }
    else {
      text += exports.fetch(kls.toLowerCase() + '.' + method);
    }

    // And tack on spaces, commas, etc. left over in the string
    if (!!etc) {
      text += etc;
    }
    match = re.exec(str);
  } while (match);
  return text;
};

/**
 * Charlatan.numerify(source) -> string
 * - source (string): source string
 *
 * Find `#` symbol and replace it with random number
 **/
exports.numerify = function (source) {
  return source.replace(/\#/g, function () {
    return Math.floor(Math.random() * 10);
  });
};

/**
 * Charlatan.letterify(source) -> string
 * - source (string): source string
 *
 * Find `?` symbol and replace it with random char from [A..Z]
 **/
exports.letterify = function (source) {
  return source.replace(/\?/g, function () {
    return Helpers.sample('ABCDEFGHIJKLMNOPQRSTUVWXTZ'.split(''));
  });
};

// Init Charlatan
// fetch locales and init base locale
var locales_dir = Path.resolve(__dirname, '../config/locales') + '/';

Fs.readdirSync(locales_dir).forEach(function (file) {
  if (/\.yml/.test(file)) {
    avaliable_locales[file.replace('.yml', '')] = locales_dir + file;
  }
});

exports.setLocale('en');
<commit_msg>linting cleanup<commit_after>'use strict';

/**
 * class Charlatan
 *
 **/

exports.Name        = require('./charlatan/name');
exports.Address     = require('./charlatan/address');
exports.PhoneNumber = require('./charlatan/phone_number');
exports.Internet    = require('./charlatan/internet');
exports.Company     = require('./charlatan/company');
exports.Lorem       = require('./charlatan/lorem');
var Helpers         = exports.Helpers = require('./charlatan/helpers');

require('js-yaml');
var Fs = require('fs');
var Path = require('path');

// locale files {<<locale_name>>: <<path_locale_file>>}
var avaliable_locales = {};

// already loaded locales {<<locale_name>>: <<locale_data>>}
var locales = {};

var current_locale;
var base_locale = 'en';

// getValue(data, path) -> mixed
// - data (object): locale
// - path (array): path to property
//
// get value from locale hash
function getValue(data, path) {
  if (!data[path[0]] ||
      (path.length > 0 && typeof(data[path[0]]) !== 'object')) {
    return undefined;
  }
  if (path.length === 1) {
    return data[path[0]];
  }
  return getValue(data[path[0]], path.slice(1));
}

// snake2camelcase(str) -> string
// - str (string): source string
//
// locale files use snake notation `xxx_yyy_zzz`
// but real methods use camelcase `xxxYyyZzz`
function snake2camelcase(str) {
  var parts = str.split('_');
  if (parts.length === 1) {
    return str;
  }
  return parts[0] + parts.slice(1).map(function (word) {
    return word.charAt(0).toUpperCase() + word.slice(1);
  }).join('');
}

/**
 * Charlatan.addLocale(name, data) -> void
 * - name (string): locale name, for example `en-us`
 * - data (object|string): locale hash or yaml file
 *
 * Load locale to Charlatan. Charlatan preload some
 * [locales](https://github.com/nodeca/charlatan/tree/master/lib)
 **/
var addLocale = exports.addLocale = function (name, data) {
  // if data not set, then check avaliable
  if (data === undefined) {
    if (!avaliable_locales[name]) {
      throw new Error('Locale not found:' + name);
    }

    data = avaliable_locales[name];

    // load base language, if exists
    var lang = name.split('-')[0];
    if (name !== lang && !!avaliable_locales[lang]) {
      addLocale(lang);
    }
  }
  // data in file
  if (typeof data === 'string') {
    if (!Fs.existsSync(data)) {
      throw new Error('Config ' + data + ' not found');
    }
    data = require(data)[name];
  }
  locales[name] = data;
  return true;
};

/**
 * Charlatan.setLocale(name) -> void
 * - name (string): locale name, for example `en-us`
 *
 *
 **/
exports.setLocale = function (name) {
  if (!locales[name]) {
    addLocale(name);
  }
  current_locale = name;
  return true;
};

/**
 * Charlatan.translate(key) -> mixed
 * - key (string): key in locale, for example `address.city_prefix`
 *
 * Returns a phrase from the locale.
 *
 * Fallbacks to base language (and then to `en`)
 *   if it was not found: `ru_RU -> ru -> en`
 **/
exports.translate = function (key) {
  var lang = current_locale.split('-')[0];
  var path = key.split('.');
  var result = getValue(locales[current_locale], path);

  // fallback to lang
  if (result === undefined && lang !== current_locale) {
    result = getValue(locales[lang], path);
  }

  // fallback to base_lang
  if (result === undefined) {
    result = getValue(locales[base_locale], path);
  }

  // oops
  if (result === undefined) {
    throw new Error('Invalid path:' + key);
  }

  return result;
};

/**
 * Charlatan.fetch(key) -> mixed
 * - key (string): key in locale, for example `address.city_prefix`
 *
 * Helper for the common approach of grabbing a translation
 * with an array of values and selecting one of them.
 **/
var fetch = exports.fetch = function (key) {
  var fetched = exports.translate(key);
  if (fetched.length !== undefined) {
    fetched = Helpers.sample(fetched);
  }
  // need regexfy from ruby version?
  return fetched;
};

/**
 * Charlatan.parse(key) -> string
 * - key (string): key in locale, for example `address.city_prefix`
 *
 * Load formatted strings from the locale, "parsing" them
 * into method calls that can be used to generate a
 * formatted translation: e.g., "#{first_name} #{last_name}".
 **/
exports.parse = function (key) {
  var match, kls, method, etc;
  var re = /#\{([A-Za-z]+(?:\.))?([^\}]+)\}([^#]+)?/g;
  var str = fetch(key);
  var text = '';

  match = re.exec(str);
  if (!match) {
    return str;
  }
  do {
    kls = match[1];
    method = match[2];
    etc = match[3];
    // If the token had a class Prefix (e.g., Name.first_name)
    // take it, otherwise use base namespace part
    if (!kls) {
      kls = exports.Helpers.capitalize(key.split('.')[0]);
    }
    else {
      kls = kls.slice(0, -1);
    }
    // If the class has the method, call it, otherwise
    // fetch the transation
    if (!!exports[kls] &&
        typeof(exports[kls][snake2camelcase(method)]) === 'function') {

      text += exports[kls][snake2camelcase(method)].call(exports[kls]);
    }
    else {
      text += exports.fetch(kls.toLowerCase() + '.' + method);
    }

    // And tack on spaces, commas, etc. left over in the string
    if (!!etc) {
      text += etc;
    }
    match = re.exec(str);
  } while (match);
  return text;
};

/**
 * Charlatan.numerify(source) -> string
 * - source (string): source string
 *
 * Find `#` symbol and replace it with random number
 **/
exports.numerify = function (source) {
  return source.replace(/\#/g, function () {
    return Math.floor(Math.random() * 10);
  });
};

/**
 * Charlatan.letterify(source) -> string
 * - source (string): source string
 *
 * Find `?` symbol and replace it with random char from [A..Z]
 **/
exports.letterify = function (source) {
  return source.replace(/\?/g, function () {
    return Helpers.sample('ABCDEFGHIJKLMNOPQRSTUVWXTZ'.split(''));
  });
};

// Init Charlatan
// fetch locales and init base locale
var locales_dir = Path.resolve(__dirname, '../config/locales') + '/';

Fs.readdirSync(locales_dir).forEach(function (file) {
  if (/\.yml/.test(file)) {
    avaliable_locales[file.replace('.yml', '')] = locales_dir + file;
  }
});

exports.setLocale('en');
<|endoftext|>