<commit_before>module.exports = new (function(){
  var db, btcd
  var that = this

  this.setup = function(_db, _btcd, _rpc_server) {
    db = _db
    btcd = _btcd
  }

  this.sync = function(){
    db.load_block_hash(function(block_hash){
      btcd.unprocessed_transactions_since(block_hash, function(bitcoin){
        db.transaction(function(){
          bitcoin.transactions.forEach(function(tx){
            db.add_bitcoin_tx(tx)
          })
          lastblock = bitcoin.lastblock
          db.save_block_hash(lastblock)
          that.block_report(lastblock)
        })
      })
    })
  }

  this.block_report = function(block_hash){
    btcd.block(block_hash, function(block){
      console.log("Synced up to block #"+block.height+" "+(new Date(block.time*1000)))
    })
  }

  this.balance = function(username){
    return {username: username, balance: {amount: 3, currency: 'btc'}}
  }
})
<commit_msg>first bits of an rpc method<commit_after>module.exports = new (function(){
  var db, btcd
  var that = this

  this.setup = function(_db, _btcd, _rpc_server) {
    db = _db
    btcd = _btcd
  }

  this.sync = function(){
    db.load_block_hash(function(block_hash){
      btcd.unprocessed_transactions_since(block_hash, function(bitcoin){
        db.transaction(function(){
          bitcoin.transactions.forEach(function(tx){
            db.add_bitcoin_tx(tx)
          })
          lastblock = bitcoin.lastblock
          db.save_block_hash(lastblock)
          that.block_report(lastblock)
        })
      })
    })
  }

  this.block_report = function(block_hash){
    btcd.block(block_hash, function(block){
      console.log("Synced up to block #"+block.height+" "+(new Date(block.time*1000)))
    })
  }

  this.balance = function(username){
    return {username: username,
            balance: {amount: 3, currency: 'btc'},
            now: Date.now()}
  }
})
<|endoftext|>


<commit_before>var _ = require("underscore")
  , inflection = require("./inflection")

var config = {}
CollectionFactory.configure = function(conf) {
  config = conf
}

function CollectionFactory(name, selector) {
  this.name = name
  this.selector = selector
}

CollectionFactory.prototype.setTemplateFactory = function(TemplateFactory) {
  this.TemplateFactory = TemplateFactory
  return TemplateFactory
}

CollectionFactory.prototype.mapView = function(key) {
  this.viewClassName = key
}

CollectionFactory.prototype.generate = function(parent, parentView) {
  var that = this

  function Collection(models, opts) {
    opts = opts || {}
    this.length = 0
    this.Template = that.TemplateFactory.generate()
    this.templates = []
    this.container = parent.contents().andSelf().filter(that.selector)

    if(that.viewClassName) {
      this.View = require(config.viewDirectory + "/" + inflection.underscore(that.viewClassName))
    }

    if(models) {
      this.bind(models, opts.parentView)
    }
  }

  Collection.prototype.bind = function(collection, parentView) {
    if(this.View) {
      opts = { parent: parentView, el: this.container, template: this, collection: collection }
      this.view = new this.View(opts)
    }
    if(_.isArray(collection)) {
      this.push(collection)
    } else {
      this.push(collection.models)
      collection.on("add", function(model, collection, opts) {
        this.add(model, { at: opts.index })
      }, this)

      collection.on("remove", function(model, collection, opts) {
        var index = opts.index
        this.remove(this.at(index))
      }, this)
    }
  }

  Collection.prototype.remove = function(template) {
    if(_.isArray(template)) {
      return _(template).each(this.remove, this)
    }
    var index = _(this.templates).indexOf(template)
    if(index < 0) {
      throw new Error("Cannot remove a template that's not in collection")
    }
    this.templates.splice(index, 1)
    this.container.children().eq(index).remove()

    this.length = this.templates.length
  }

  Collection.prototype.at = function(index) {
    return this.templates[index]
  }

  Collection.prototype.add = function(models, opts) {
    opts = opts || {}
    opts.at = opts.at || this.templates.length
    if(!_.isArray(models)) {
      models = [models]
    }
    var templates = _.map(models, function(model) {
       return new this.Template(model)
    }, this)

    ;[].splice.apply(this.templates, [opts.at, 0].concat(templates))

    var doms = _.map(templates, function(template) {
       return template.template[0]
    })
    var children = this.container.children()
    if(children.length)
      $(doms).insertAfter(children[opts.at - 1])
    else
      this.container.append($(doms))

    this.length = templates.length
  }

  Collection.prototype.push = function(data) {
    if(_.isArray(data)) {
      return _(data).each(this.push, this)
    }
    var template = new this.Template(data)

    this.templates.push(template)
    this.container.append(template.template)
    this.length = this.templates.length
  }

  return Collection
}

module.exports = CollectionFactory
<commit_msg>pass the parent view along to any sub templates of a collection<commit_after>var _ = require("underscore")
  , inflection = require("./inflection")

var config = {}
CollectionFactory.configure = function(conf) {
  config = conf
}

function CollectionFactory(name, selector) {
  this.name = name
  this.selector = selector
}

CollectionFactory.prototype.setTemplateFactory = function(TemplateFactory) {
  this.TemplateFactory = TemplateFactory
  return TemplateFactory
}

CollectionFactory.prototype.mapView = function(key) {
  this.viewClassName = key
}

CollectionFactory.prototype.generate = function(parent, parentView) {
  var that = this

  function Collection(models, opts) {
    opts = opts || {}
    this.length = 0
    this.Template = that.TemplateFactory.generate()
    this.templates = []
    this.container = parent.contents().andSelf().filter(that.selector)

    if(that.viewClassName) {
      this.View = require(config.viewDirectory + "/" + inflection.underscore(that.viewClassName))
    }

    if(models) {
      this.bind(models, opts.parentView)
    }
  }

  Collection.prototype.bind = function(collection, parentView) {
    if(this.View) {
      opts = { parent: parentView, el: this.container, template: this, collection: collection }
      this.view = new this.View(opts)
    }
    if(_.isArray(collection)) {
      this.push(collection)
    } else {
      this.push(collection.models)
      collection.on("add", function(model, collection, opts) {
        this.add(model, { at: opts.index })
      }, this)

      collection.on("remove", function(model, collection, opts) {
        var index = opts.index
        this.remove(this.at(index))
      }, this)
    }
  }

  Collection.prototype.remove = function(template) {
    if(_.isArray(template)) {
      return _(template).each(this.remove, this)
    }
    var index = _(this.templates).indexOf(template)
    if(index < 0) {
      throw new Error("Cannot remove a template that's not in collection")
    }
    this.templates.splice(index, 1)
    this.container.children().eq(index).remove()

    this.length = this.templates.length
  }

  Collection.prototype.at = function(index) {
    return this.templates[index]
  }

  Collection.prototype.add = function(models, opts) {
    opts = opts || {}
    opts.at = opts.at || this.templates.length
    if(!_.isArray(models)) {
      models = [models]
    }
    var templates = _.map(models, function(model) {
      return new this.Template(model, { parentView: this.view })
    }, this)

    ;[].splice.apply(this.templates, [opts.at, 0].concat(templates))

    var doms = _.map(templates, function(template) {
       return template.template[0]
    })
    var children = this.container.children()
    if(children.length)
      $(doms).insertAfter(children[opts.at - 1])
    else
      this.container.append($(doms))

    this.length = templates.length
  }

  Collection.prototype.push = function(data) {
    if(_.isArray(data)) {
      return _(data).each(this.push, this)
    }
    var template = new this.Template(data, { parentView: this.view })

    this.templates.push(template)
    this.container.append(template.template)
    this.length = this.templates.length
  }

  return Collection
}

module.exports = CollectionFactory
<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var util = require('./util')
  , debug = require('debug')('monk:queries')
  , EventEmitter = require('events').EventEmitter
  , Promise = require('./promise');

/**
 * Module exports
 */

module.exports = Collection;

/**
 * Collection.
 *
 * @api public
 */

function Collection (manager, name) {
  this.manager = manager;
  this.driver = manager.driver;
  this.name = name;
  this.col = this.driver.collection(name);
  this.options = {};
  this.col.emitter.setMaxListeners(Infinity);
}

/**
 * Inherits from EventEmitter.
 */

Collection.prototype.__proto__ = EventEmitter.prototype;

/**
 * Casts to objectid
 *
 * @param {Mixed} hex id or ObjectId
 * @return {ObjectId}
 * @api public
 */

Collection.prototype.id =
Collection.prototype.oid = function (str) {
  if (null == str) return this.col.ObjectID();
  return 'string' == typeof str ? this.col.id(str) : str;
};

/**
 * Opts utility.
 */

Collection.prototype.opts = function (opts) {
  opts = util.options(opts || {});

  for (var i in this.manager.options) {
    if (!(i in opts) && !(i in this.options)) {
      opts[i] = this.manager.options[i];
    }
  }

  for (var i in this.options) {
    if (!(i in opts)) {
      opts[i] = this.options[i];
    }
  }

  return opts;
};

/**
 * Set up indexes.
 *
 * @param {Object|String|Array} fields
 * @param {Object|Function} optional, options or callback
 * @param {Function} optional, callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.index =
Collection.prototype.ensureIndex = function (fields, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  var fields = util.fields(fields)
    , opts = opts || {}
    , promise = new Promise(this, 'index');

  if (fn) {
    promise.complete(fn);
  }

  // query
  debug('%s ensureIndex "%j" ("%j")', this.name, fields, opts);
  this.col.ensureIndex(fields, opts, promise.fulfill);

  return promise;
};

/**
 * Gets all indexes.
 *
 * @param {Function} callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.indexes = function (fn) {
  var promise = new Promise(this, 'indexes');

  if (fn) {
    promise.complete(fn);
  }

  // query
  debug('%s indexInformation', this.name);
  this.col.indexInformation(promise.fulfill);

  return promise;
};

/**
 * update
 *
 * @param {Object} search query
 * @param {Object} update obj
 * @param {Object|String|Array} optional, options or fields
 * @param {Function} callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.update = function (search, update, opts, fn) {
  if ('string' == typeof search || 'function' == typeof search.toHexString) {
    return this.update({ _id: search }, update, opts, fn);
  }

  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  var promise = new Promise(this, 'update');

  if (fn) {
    promise.on('complete', fn);
  }

  // cast
  search = this.cast(search);
  update = this.cast(update);

  // query
  debug('%s update %j with %j', this.name, search, update);
  opts = this.opts(opts);

  var callback = opts.safe ? promise.fulfill : function () {
    // node-mongodb-native will send err=undefined and call the fn
    // in the same tick if safe: false
    var args = arguments;
    args[0] = args[0] || null;
    process.nextTick(function () {
      promise.fulfill.apply(promise, args);
    });
  };

  this.col.update(search, update, opts, callback);

  return promise;
};

/**
 * update by id helper
 *
 * @param {String|Object} object id
 * @param {Object} update obj
 * @param {Object|String|Array} optional, options or fields
 * @param {Function} callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.updateById = function (id, obj, opts, fn) {
  return this.update({ _id: id }, obj, opts, fn);
};

/**
 * remove
 *
 * @param {Object} search query
 * @param {Object|Function} optional, options or callback
 * @param {Function} optional, callback
 * @return {Promise}
 */

Collection.prototype.remove = function (search, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  var promise = new Promise(this, 'remove');

  if (fn) {
    promise.on('complete', fn);
  }

  // cast
  search = this.cast(search);

  // query
  debug('%s remove "%j" with "%j"', this.name, search, opts);
  this.col.remove(search, this.opts(opts), promise.fulfill);

  return promise;
};

/**
 * findAndModify
 *
 * @param {Object} search query, or { query, update } object
 * @param {Object} optional, update object
 * @param {Object|String|Array} optional, options or fields
 * @param {Function} callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.findAndModify = function (query, update, opts, fn) {
  var promise = new Promise(this, 'findAndModify');

  query = query || {};

  if ('object' != typeof query.query && 'object' != typeof query.update) {
    query = {
        query: query
      , update: update
    };
  } else {
    opts = update;
    fn = opts;
  }

  if ('string' == typeof query.query || 'function' == typeof query.query.toHexString) {
    query.query = { _id: query.query };
  }

  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = opts || {};

  // `new` defaults to `true` for upserts
  if (null == opts.new && opts.upsert) {
    opts.new = true;
  }

  if (fn) {
    promise.on('complete', fn);
  }

  // cast
  query.query = this.cast(query.query);
  query.update = this.cast(query.update);

  // query
  debug('%s findAndModify "%j" with "%j"', this.name, query.query, query.update);
  this.col.findAndModify(
      query.query
    , []
    , query.update
    , this.opts(opts)
    , promise.fulfill
  );

  return promise;
};

/**
 * insert
 *
 * @param {Object} data
 * @param {Object|String|Array} optional, options or fields
 * @param {Function} callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.insert = function (data, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  var promise = new Promise(this, 'insert');

  if (fn) {
    promise.complete(fn);
  }

  // cast
  data = this.cast(data);

  // query
  debug('%s insert "%j"', this.name, data);
  this.col.insert(data, this.opts(opts), function (err, docs) {
    process.nextTick(function () {
      promise.fulfill.call(promise, err, docs ? docs[0] : docs);
    });
  });

  return promise;
};

/**
 * findOne by ID
 *
 * @param {String} hex id
 * @param {Object|String|Array} optional, options or fields
 * @param {Function} completion callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.findById = function (id, opts, fn) {
  return this.findOne({ _id: id }, opts, fn);
};

/**
 * find
 *
 * @param {Object} query
 * @param {Object|String|Array} optional, options or fields
 * @param {Function} completion callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.find = function (query, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  var promise = new Promise(this, 'find');

  if (fn) {
    promise.complete(fn);
  }

  // cast
  query = this.cast(query);

  // opts
  opts = this.opts(opts);

  // query
  debug('%s find "%j"', this.name, query);
  var cursor = this.col.find(query, opts);

  if (null == opts.stream) {
    process.nextTick(function () {
      if (promise.listeners('each').length) {
        stream();
      } else {
        cursor.toArray(promise.fulfill);
      }
    });
  } else if (opts.stream) {
    stream();
  } else {
    cursor.toArray(promise.fulfill);
  }

  function stream () {
    cursor.each(function (err, doc) {
      if (err) {
        promise.emit('error', err);
      } else if (doc) {
        promise.emit('each', doc);
      } else {
        promise.emit('success');
      }
    });
  }

  return promise;
};

/**
 * count
 *
 * @param {Object} query
 * @param {Function} completion callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.count = function (query, fn) {
  var promise = new Promise(this, 'find');

  if (fn) {
    promise.complete(fn);
  }

  // cast
  query = this.cast(query);

  // query
  debug('%s count "%j"', this.name, query);
  this.col.count(query, promise.fulfill);

  return promise;
};

/**
 * findOne
 *
 * @param {String|ObjectId|Object} query
 * @param {Object} options
 * @param {Function} completion callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.findOne = function (search, opts, fn) {
  search = search || {};

  if ('string' == typeof search || 'function' == typeof search.toHexString) {
    return this.findById(search, opts, fn);
  }

  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  var promise = new Promise(this, 'findOne');

  if (fn) {
    promise.complete(fn);
  }

  // cast
  search = this.cast(search);

  // query
  debug('findOne "%j"', search);
  this.col.findOne(search, this.opts(opts), promise.fulfill);

  return promise;
};

/**
 * Applies ObjectId casting to _id fields.
 *
 * @param {Object} optional, query
 * @return {Object} query
 * @api private
 */

Collection.prototype.cast = function (obj) {
  obj = obj || {};

  if (obj._id) {
    obj._id = this.id(obj._id);
  }

  if (obj.$set && obj.$set._id) {
    obj.$set._id = this.id(obj.$set._id);
  }

  return obj;
};

/**
 * Drops the collection.
 *
 * @param {Function} optional, callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.drop = function (fn) {
  var promise = new Promise(this, 'drop');

  if (fn) {
    promise.complete(fn);
  }

  this.col.drop(promise.fulfill);

  return promise;
};
<commit_msg>collection: pass options to promises<commit_after>
/**
 * Module dependencies.
 */

var util = require('./util')
  , debug = require('debug')('monk:queries')
  , EventEmitter = require('events').EventEmitter
  , Promise = require('./promise');

/**
 * Module exports
 */

module.exports = Collection;

/**
 * Collection.
 *
 * @api public
 */

function Collection (manager, name) {
  this.manager = manager;
  this.driver = manager.driver;
  this.name = name;
  this.col = this.driver.collection(name);
  this.options = {};
  this.col.emitter.setMaxListeners(Infinity);
}

/**
 * Inherits from EventEmitter.
 */

Collection.prototype.__proto__ = EventEmitter.prototype;

/**
 * Casts to objectid
 *
 * @param {Mixed} hex id or ObjectId
 * @return {ObjectId}
 * @api public
 */

Collection.prototype.id =
Collection.prototype.oid = function (str) {
  if (null == str) return this.col.ObjectID();
  return 'string' == typeof str ? this.col.id(str) : str;
};

/**
 * Opts utility.
 */

Collection.prototype.opts = function (opts) {
  opts = util.options(opts || {});

  for (var i in this.manager.options) {
    if (!(i in opts) && !(i in this.options)) {
      opts[i] = this.manager.options[i];
    }
  }

  for (var i in this.options) {
    if (!(i in opts)) {
      opts[i] = this.options[i];
    }
  }

  return opts;
};

/**
 * Set up indexes.
 *
 * @param {Object|String|Array} fields
 * @param {Object|Function} optional, options or callback
 * @param {Function} optional, callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.index =
Collection.prototype.ensureIndex = function (fields, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  var fields = util.fields(fields)
    , opts = opts || {}
    , promise = new Promise(this, 'index');

  if (fn) {
    promise.complete(fn);
  }

  // query
  debug('%s ensureIndex "%j" ("%j")', this.name, fields, opts);
  this.col.ensureIndex(fields, opts, promise.fulfill);

  return promise;
};

/**
 * Gets all indexes.
 *
 * @param {Function} callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.indexes = function (fn) {
  var promise = new Promise(this, 'indexes');

  if (fn) {
    promise.complete(fn);
  }

  // query
  debug('%s indexInformation', this.name);
  this.col.indexInformation(promise.fulfill);

  return promise;
};

/**
 * update
 *
 * @param {Object} search query
 * @param {Object} update obj
 * @param {Object|String|Array} optional, options or fields
 * @param {Function} callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.update = function (search, update, opts, fn) {
  if ('string' == typeof search || 'function' == typeof search.toHexString) {
    return this.update({ _id: search }, update, opts, fn);
  }

  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = this.opts(opts);
  var promise = new Promise(this, 'update', opts);

  if (fn) {
    promise.on('complete', fn);
  }

  // cast
  search = this.cast(search);
  update = this.cast(update);

  // query
  debug('%s update %j with %j', this.name, search, update);

  var callback = opts.safe ? promise.fulfill : function () {
    // node-mongodb-native will send err=undefined and call the fn
    // in the same tick if safe: false
    var args = arguments;
    args[0] = args[0] || null;
    process.nextTick(function () {
      promise.fulfill.apply(promise, args);
    });
  };

  this.col.update(search, update, opts, callback);

  return promise;
};

/**
 * update by id helper
 *
 * @param {String|Object} object id
 * @param {Object} update obj
 * @param {Object|String|Array} optional, options or fields
 * @param {Function} callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.updateById = function (id, obj, opts, fn) {
  return this.update({ _id: id }, obj, opts, fn);
};

/**
 * remove
 *
 * @param {Object} search query
 * @param {Object|Function} optional, options or callback
 * @param {Function} optional, callback
 * @return {Promise}
 */

Collection.prototype.remove = function (search, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = this.opts(opts);
  var promise = new Promise(this, 'remove', opts);

  if (fn) {
    promise.on('complete', fn);
  }

  // cast
  search = this.cast(search);

  // query
  debug('%s remove "%j" with "%j"', this.name, search, opts);
  this.col.remove(search, opts, promise.fulfill);

  return promise;
};

/**
 * findAndModify
 *
 * @param {Object} search query, or { query, update } object
 * @param {Object} optional, update object
 * @param {Object|String|Array} optional, options or fields
 * @param {Function} callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.findAndModify = function (query, update, opts, fn) {
  query = query || {};

  if ('object' != typeof query.query && 'object' != typeof query.update) {
    query = {
        query: query
      , update: update
    };
  } else {
    opts = update;
    fn = opts;
  }

  if ('string' == typeof query.query || 'function' == typeof query.query.toHexString) {
    query.query = { _id: query.query };
  }

  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = opts || {};

  // `new` defaults to `true` for upserts
  if (null == opts.new && opts.upsert) {
    opts.new = true;
  }

  var promise = new Promise(this, 'findAndModify', opts);

  if (fn) {
    promise.on('complete', fn);
  }

  // cast
  query.query = this.cast(query.query);
  query.update = this.cast(query.update);

  // query
  debug('%s findAndModify "%j" with "%j"', this.name, query.query, query.update);
  this.col.findAndModify(
      query.query
    , []
    , query.update
    , this.opts(opts)
    , promise.fulfill
  );

  return promise;
};

/**
 * insert
 *
 * @param {Object} data
 * @param {Object|String|Array} optional, options or fields
 * @param {Function} callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.insert = function (data, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = this.opts(opts);
  var promise = new Promise(this, 'insert', opts);

  if (fn) {
    promise.complete(fn);
  }

  // cast
  data = this.cast(data);

  // query
  debug('%s insert "%j"', this.name, data);
  this.col.insert(data, opts, function (err, docs) {
    process.nextTick(function () {
      promise.fulfill.call(promise, err, docs ? docs[0] : docs);
    });
  });

  return promise;
};

/**
 * findOne by ID
 *
 * @param {String} hex id
 * @param {Object|String|Array} optional, options or fields
 * @param {Function} completion callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.findById = function (id, opts, fn) {
  return this.findOne({ _id: id }, opts, fn);
};

/**
 * find
 *
 * @param {Object} query
 * @param {Object|String|Array} optional, options or fields
 * @param {Function} completion callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.find = function (query, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  // cast
  query = this.cast(query);

  // opts
  opts = this.opts(opts);

  // query
  debug('%s find "%j"', this.name, query);
  var cursor = this.col.find(query, opts);

  // promise
  var promise = new Promise(this, 'find', opts);

  if (fn) {
    promise.complete(fn);
  }

  if (null == opts.stream) {
    process.nextTick(function () {
      if (promise.listeners('each').length) {
        stream();
      } else {
        cursor.toArray(promise.fulfill);
      }
    });
  } else if (opts.stream) {
    stream();
  } else {
    cursor.toArray(promise.fulfill);
  }

  function stream () {
    cursor.each(function (err, doc) {
      if (err) {
        promise.emit('error', err);
      } else if (doc) {
        promise.emit('each', doc);
      } else {
        promise.emit('success');
      }
    });
  }

  return promise;
};

/**
 * count
 *
 * @param {Object} query
 * @param {Function} completion callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.count = function (query, fn) {
  var promise = new Promise(this, 'find');

  if (fn) {
    promise.complete(fn);
  }

  // cast
  query = this.cast(query);

  // query
  debug('%s count "%j"', this.name, query);
  this.col.count(query, promise.fulfill);

  return promise;
};

/**
 * findOne
 *
 * @param {String|ObjectId|Object} query
 * @param {Object} options
 * @param {Function} completion callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.findOne = function (search, opts, fn) {
  search = search || {};

  if ('string' == typeof search || 'function' == typeof search.toHexString) {
    return this.findById(search, opts, fn);
  }

  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = this.opts(opts);
  var promise = new Promise(this, 'findOne', opts);

  if (fn) {
    promise.complete(fn);
  }

  // cast
  search = this.cast(search);

  // query
  debug('findOne "%j"', search);
  this.col.findOne(search, opts, promise.fulfill);

  return promise;
};

/**
 * Applies ObjectId casting to _id fields.
 *
 * @param {Object} optional, query
 * @return {Object} query
 * @api private
 */

Collection.prototype.cast = function (obj) {
  obj = obj || {};

  if (obj._id) {
    obj._id = this.id(obj._id);
  }

  if (obj.$set && obj.$set._id) {
    obj.$set._id = this.id(obj.$set._id);
  }

  return obj;
};

/**
 * Drops the collection.
 *
 * @param {Function} optional, callback
 * @return {Promise}
 * @api public
 */

Collection.prototype.drop = function (fn) {
  var promise = new Promise(this, 'drop');

  if (fn) {
    promise.complete(fn);
  }

  this.col.drop(promise.fulfill);

  return promise;
};
<|endoftext|>


<commit_before>module.exports = getConfig

const defaultsDeep = require('lodash').defaultsDeep
const validate = require('joi').validate

const getDefaults = require('./get-defaults')
const getEnv = require('./get-env')
const parseOptions = require('./parse-options')
const schema = require('./schemas')

function getConfig (options, callback) {
  if (typeof options === 'function') {
    callback = options
    options = {}
  }
  const config = defaultsDeep(parseOptions(options), getEnv(), getDefaults())

  // amend webdriver config based on runner
  defaultsDeep(config.webdriver, config[config.runner].webdriver)
  if (config.runner === 'saucelabs') {
    config.webdriver.desiredCapabilities.name += ` - ${config.saucelabs.connect.tunnelIdentifier}`
  }

  if (config.runner !== 'selenium' && config.runner !== 'saucelabs') {
    return callback(new Error(`options.runner must be "selenium" or "saucelabs", but it is ${config.runner}`))
  }

  validate(config, schema[config.runner], (error) => {
    callback(error, config)
  })
}
<commit_msg>fix: don’t crash when runner is invalid<commit_after>module.exports = getConfig

const defaultsDeep = require('lodash').defaultsDeep
const validate = require('joi').validate

const getDefaults = require('./get-defaults')
const getEnv = require('./get-env')
const parseOptions = require('./parse-options')
const schema = require('./schemas')

function getConfig (options, callback) {
  if (typeof options === 'function') {
    callback = options
    options = {}
  }
  const config = defaultsDeep(parseOptions(options), getEnv(), getDefaults())

  if (config.runner !== 'selenium' && config.runner !== 'saucelabs') {
    return callback(new Error(`options.runner must be "selenium" or "saucelabs", but it is "${config.runner}"`))
  }

  // amend webdriver config based on runner
  defaultsDeep(config.webdriver, config[config.runner].webdriver)
  if (config.runner === 'saucelabs') {
    config.webdriver.desiredCapabilities.name += ` - ${config.saucelabs.connect.tunnelIdentifier}`
  }

  validate(config, schema[config.runner], (error) => {
    callback(error, config)
  })
}
<|endoftext|>


<commit_before>'use strict'

const Socket = require('./connections/socket.js')
const Http = require('./connections/http.js')
const request = require('request')
const Queue = require('./queue.js')
let queue = null

class Connection {

    /**
     * Setup client
     */
    setup(options) {
        return new Promise((resolve, reject) => {

            this.options = options
            queue = new Queue(options)

            /**
             * Get access token if credentials are provided
             */
            if (options.user_key && options.user_secret) {

                // Set authorization token for supplied user data
                this.getToken(options.user_key, options.user_secret)

                // Set headers with received token
                .then(() => this.setHeaders(this.access_token))

                // Apply headers to client
                .then(() => this.setClient(options.use_socket))

                // Resolve Promise
                .then(() => resolve())
            }

            /**
             * Credentials not provided
             */
            else {

                // Set connection target & connect w/ client
                this.auth_options = {
                    namespace: this.options.namespace,
                    api_url: this.options.api_url,
                    auth_url: this.options.auth_url
                }
                this.setClient(options.use_socket)

                // Resolve Promise
                .then(() => resolve())
            }

        })
    }

    /**
     * Get Token on initial call and expiration
     * Always uses http /auth endpoint
     */
    getToken(user_key, user_secret) {
        return new Promise((resolve, reject) => {

            // Set authentication options
            let auth_request = {
                user_key: user_key,
                user_secret: user_secret
            }

            // Post Options
            let post_options = {
                method: 'post',
                body: auth_request,
                json: true
            }

            // Send to /auth endpoint
            request(this.options.auth_url + 'token', post_options, (err, res) => {
                if (err) return err

                // Save token in class object
                this.access_token = res.body.access_token
                this.refresh_token = res.body.refresh_token

                resolve()
            })
        })
    }

    /**
     * Config Headers for request/socket
     * Leave independent param <token>. May be useful for manual action.
     */
    setHeaders(token) {
        if (!token) token = this.access_token

        this.auth_options = {
            token: token,
            namespace: this.options.namespace,
            api_url: this.options.api_url,
            auth_url: this.options.auth_url
        }
    }

    /**
     * Compare connection type, then create new client with previous options
     */
    setClient(use_socket) {
        return new Promise((resolve, reject) => {

            // Use appropriate client (default: socket)
            if (use_socket) {
                Socket.config(this.auth_options, resolve, reject)
                this.client = Socket
            } else {
                Http.config(this.auth_options, resolve, reject)
                this.client = Http
            }
        })
    }

    /**
     * Get new access token from refresh_token & save in object
     */
    refreshToken(token) {
        return new Promise((resolve, reject) => {
            if (!token) token = this.refresh_token

            // Refresh state for multiple failed requests
            if (!this.refreshing) {
                this.refreshing = true

                // Set authentication options
                let auth_request = {
                    refresh_token: token
                }

                // Post Options
                let post_options = {
                    method: 'post',
                    body: auth_request,
                    json: true
                }

                // Send to /auth endpoint
                request(this.options.auth_url + 'token', post_options, (err, res) => {
                    if (err) reject(err)

                    // Save token in class object
                    this.access_token = res.body.access_token
                    this.setHeaders()
                    this.refreshing = false
                    this.setClient(this.options.use_socket).then(() => resolve())
                })
            }

            // Already refreshing? -> Add to queue
            else resolve()
        })
    }

    /**
     * RESTful methods
     */
    request(verb, query) {
        return new Promise((resolve, reject) => {

            // Avoid rate limit errors if not disabled
            queue.throttle()

            // Let Connection send request
            .then(() => this.client.send(verb, query))

            // Check if Response is error
            .then(res => this.errCheck(res, verb, query))

            // Res is potentially modified (non-err) res from errCheck
            .then(res => resolve(res))
            .catch(err => reject(err))
        })
    }

    /**
     * Handles Error Responses
     */
    errCheck(res, verb, query) {
        return new Promise((resolve, reject) => {
            let statusPrefix = res.statusCode ? res.statusCode.toString()[0] : null

            // Response not 2xx?
            if (res.body && statusPrefix !== '2') {

                // If expired: Get new token w/ refresh token & retry method
                if (res.body.toString().includes('jwt expired')) {
                    this.refreshToken()

                    // Retry original method
                    .then(() => this.request(verb, query))

                    // Modify response for main
                    .then((res) => resolve(res))
                }

                // Rate Limited
                else if (res.body.toString().includes("Rate limit")) {

                    // Rejection due to frequency
                    if (res.body.toString().includes('Request intervals too close')) {
                        queue.delay(queue.delayDiff)
                            .then(() => this.request(verb, query))
                            .then((res) => resolve(res))
                    }

                    // Rejection due to empty token bucket
                    if (res.body.toString().includes('Max requests per interval reached')) {
                        queue.delay(queue.delayMax)
                            .then(() => this.request(verb, query))
                            .then((res) => resolve(res))
                    }
                }

                // Unhandled error
                else resolve(res)
            }

            // No Error
            else resolve(res)
        })
    }
}

module.exports = Connection
<commit_msg>Retry if nodes are busy<commit_after>'use strict'

const Socket = require('./connections/socket.js')
const Http = require('./connections/http.js')
const request = require('request')
const Queue = require('./queue.js')
let queue = null

class Connection {

    /**
     * Setup client
     */
    setup(options) {
        return new Promise((resolve, reject) => {

            this.options = options
            queue = new Queue(options)

            /**
             * Get access token if credentials are provided
             */
            if (options.user_key && options.user_secret) {

                // Set authorization token for supplied user data
                this.getToken(options.user_key, options.user_secret)

                // Set headers with received token
                .then(() => this.setHeaders(this.access_token))

                // Apply headers to client
                .then(() => this.setClient(options.use_socket))

                // Resolve Promise
                .then(() => resolve())
            }

            /**
             * Credentials not provided
             */
            else {

                // Set connection target & connect w/ client
                this.auth_options = {
                    namespace: this.options.namespace,
                    api_url: this.options.api_url,
                    auth_url: this.options.auth_url
                }
                this.setClient(options.use_socket)

                // Resolve Promise
                .then(() => resolve())
            }

        })
    }

    /**
     * Get Token on initial call and expiration
     * Always uses http /auth endpoint
     */
    getToken(user_key, user_secret) {
        return new Promise((resolve, reject) => {

            // Set authentication options
            let auth_request = {
                user_key: user_key,
                user_secret: user_secret
            }

            // Post Options
            let post_options = {
                method: 'post',
                body: auth_request,
                json: true
            }

            // Send to /auth endpoint
            request(this.options.auth_url + 'token', post_options, (err, res) => {
                if (err) return err

                // Save token in class object
                this.access_token = res.body.access_token
                this.refresh_token = res.body.refresh_token

                resolve()
            })
        })
    }

    /**
     * Config Headers for request/socket
     * Leave independent param <token>. May be useful for manual action.
     */
    setHeaders(token) {
        if (!token) token = this.access_token

        this.auth_options = {
            token: token,
            namespace: this.options.namespace,
            api_url: this.options.api_url,
            auth_url: this.options.auth_url
        }
    }

    /**
     * Compare connection type, then create new client with previous options
     */
    setClient(use_socket) {
        return new Promise((resolve, reject) => {

            // Use appropriate client (default: socket)
            if (use_socket) {
                Socket.config(this.auth_options, resolve, reject)
                this.client = Socket
            } else {
                Http.config(this.auth_options, resolve, reject)
                this.client = Http
            }
        })
    }

    /**
     * Get new access token from refresh_token & save in object
     */
    refreshToken(token) {
        return new Promise((resolve, reject) => {
            if (!token) token = this.refresh_token

            // Refresh state for multiple failed requests
            if (!this.refreshing) {
                this.refreshing = true

                // Set authentication options
                let auth_request = {
                    refresh_token: token
                }

                // Post Options
                let post_options = {
                    method: 'post',
                    body: auth_request,
                    json: true
                }

                // Send to /auth endpoint
                request(this.options.auth_url + 'token', post_options, (err, res) => {
                    if (err) reject(err)

                    // Save token in class object
                    this.access_token = res.body.access_token
                    this.setHeaders()
                    this.refreshing = false
                    this.setClient(this.options.use_socket).then(() => resolve())
                })
            }

            // Already refreshing? -> Add to queue
            else resolve()
        })
    }

    /**
     * RESTful methods
     */
    request(verb, query) {
        return new Promise((resolve, reject) => {

            // Avoid rate limit errors if not disabled
            queue.throttle()

            // Let Connection send request
            .then(() => this.client.send(verb, query))

            // Check if Response is error
            .then(res => this.errCheck(res, verb, query))

            // Res is potentially modified (non-err) res from errCheck
            .then(res => resolve(res))
            .catch(err => reject(err))
        })
    }

    /**
     * Handles Error Responses
     */
    errCheck(res, verb, query) {
        return new Promise((resolve, reject) => {
            let statusPrefix = res.statusCode ? res.statusCode.toString()[0] : null

            // Response not 2xx?
            if (res.body && statusPrefix !== '2') {

                // If expired: Get new token w/ refresh token & retry method
                if (res.body.toString().includes('jwt expired')) {
                    this.refreshToken()

                    // Retry original method
                    .then(() => this.request(verb, query))

                    // Modify response for main
                    .then((res) => resolve(res))
                }

                // Rate Limited
                else if (res.body.toString().includes("Rate limit")) {

                    // Rejection due to frequency
                    if (res.body.toString().includes('Request intervals too close')) {
                        queue.delay(queue.delayDiff)
                            .then(() => this.request(verb, query))
                            .then((res) => resolve(res))
                    }

                    // Rejection due to empty token bucket
                    if (res.body.toString().includes('Max requests per interval reached')) {
                        queue.delay(queue.delayMax)
                            .then(() => this.request(verb, query))
                            .then((res) => resolve(res))
                    }
                }

                // Nodes are busy
                else if (res.body.toString().includes("Please try again")) {

                    // Retry original method
                    this.request(verb, query))

                    // Modify response for main
                    .then((res) => resolve(res))
                }

                // Unhandled error
                else resolve(res)
            }

            // No Error
            else resolve(res)
        })
    }
}

module.exports = Connection
<|endoftext|>


<commit_before>var base = require('base-framework'),
  _ = require('lodash'),
  Datastore = require('nedb')
  path = require('path'),
  rimraf = require('rimraf'),
  async = require('async'),
  mkdirp = require('mkdirp'),
  Query = require('./query'),
  Document = require('./document'),
  utils = require('./utils'),
  Errors = require('waterline-errors').adapter;

module.exports = base.createChild().addInstanceMethods({
  init: function (config, collections) {
    this.config = config || {};
    this.dbRoot = path.join(this.config.filePath, this.config.identity);
    this.collections = collections || {};
    this.dbs = {};

    return this;
  },

  initConn: function (cb) {
    var self = this;
    mkdirp(self.dbRoot, function (err) {
      if (err) {
        return cb(err);
      }
      async.each(_.keys(self.collections), function (key, nextCollection) {
        self.registerCollection(self.collections[key], nextCollection);
      }, cb);
    });
  },

  registerCollection: function (collection, cb) {
    this.collections[collection.identity] = collection;
    this.loadDB(collection.identity, cb);
  },

  loadDB: function (collectionID, cb) {
    var self = this,
      collection = self.collections[collectionID];

    if (!self.dbs[collectionID]) {
      self.dbs[collectionID] = new Datastore({
        filename: path.join(self.dbRoot, collectionID + '.nedb')
      });
      self.dbs[collectionID].loadDatabase(_.bind(function (err) {
        if (err) {
          return cb(err);
        }

        var dbself = this,
          def = _.clone(collection.definition);

        async.waterfall([

          function (next) {
            // Loop through the def and process attributes for each key
            var keys = _.keys(def);
            async.each(keys, _processKey, next);

            function _processKey(key, cb) {
              if (key == 'id') {
                return cb();
              }

              if (def[key].autoIncrement) {
                delete def[key].autoIncrement;
              }

              if (def[key].unique || def[key].index) {
                return dbself.ensureIndex({
                  fieldName: key,
                  sparse: true,
                  unique: def[key].unique
                }, function (err) {
                  if (err) return cb(err);
                  def[key].indexed = true;
                  cb();
                });
              }

              cb();
            }
          },
          function (next) {
            dbself.ensureIndex({
              fieldName: '__ctr',
              unique: true
            }, next);
          },
          function (next) {
            var group = {
              reduce: function (curr, result) {
                if (curr.__ctr > result.ctr) {
                  result.ctr = curr.__ctr;
                }
              },
              initial: {
                ctr: 0
              }
            };
            dbself.find({}).group(group).exec(next);
          },
          function (res, next) {
            dbself.ctr = res[0].ctr;
            self.collections[collectionID].schema = def;
            next(null, def);
          }
        ], cb);
      }, self.dbs[collectionID]));
    }
  },

  describe: function (coll, cb) {
    var des = null;
    if (!_.isEmpty(this.collections[coll]) && !_.isEmpty(this.collections[coll].schema)) {
      des = this.collections[coll].schema;
    }

    return cb(null, des);
  },

  createCollection: function (coll, definition, cb) {
    this.registerCollection({
      definition: definition,
      identity: coll
    }, cb);
  },

  dropCollection: function (coll, relations, cb) {
    var self = this;
    db = self.dbs[coll];
    db.remove({}, {
      multi: true
    }, function (err, numRemoved) {
      if (err) {
        return cb(err);
      }
      delete self.dbs[coll];
      delete self.collections[coll];
      rimraf(db.filename, cb);
    });
  },

  select: function (coll, criteria, cb) {
    var query;

    // Catch errors from building query and return to the callback
    try {
      query = new Query(criteria);
    } catch (err) {
      return cb(err);
    }

    var where = query.criteria.where || {};
    var queryOptions = _.omit(query.criteria, 'where');

    // Run Normal Query on collection
    var cursor = this.dbs[coll].find(where);

    if (query.aggregate) {
      cursor.group(query.aggregateGroup);
    }

    if (queryOptions.sort) {
      cursor.sort(queryOptions.sort);
    } else {
      cursor.sort({
        __ctr: 1
      });
    }

    if (queryOptions.skip) {
      cursor.skip(queryOptions.skip);
    }
    if (queryOptions.limit) {
      cursor.limit(queryOptions.limit);
    }

    cursor.exec(function (err, docs) {
      cb(err, utils.rewriteIds(docs));
    });

  },

  insert: function (coll, data, cb) {
    var db = this.dbs[coll];

    delete data.id;
    delete data._id;
    data.__ctr = ++db.ctr;

    db.insert(data, function (err, result) {
      if (err) return cb(err);

      result.id = result._id;
      delete result._id;
      delete result.__ctr;
      cb(err, result);
    });
  },

  insertEach: function (coll, values, cb) {
    var db = this.dbs[coll];

    _.each(values, function (data) {
      delete data.id;
      delete data._id;
      data.__ctr = ++db.ctr;
    });

    db.insert(values, function (err, result) {
      if (err) return cb(err);

      cb(err, utils.rewriteIds(result));
    });
  },

  update: function (coll, criteria, values, cb) {
    var self = this,
      query;

    // Catch errors build query and return to the callback
    try {
      query = new Query(criteria);
    } catch (err) {
      return cb(err);
    }

    values = new Document(values, this.collections[coll].schema).values;

    // NEDB doesn't allow ID's to be updated
    if (values.id) delete values.id;
    if (values._id) delete values._id;

    var updatedRecords = [];
    async.waterfall([
      // Lookup records being updated and grab their ID's
      // Useful for later looking up the record after an insert
      // Required because options may not contain an ID
      function (next) {
        self.dbs[coll].find(query.criteria.where, next);
      },
      function (records, next) {
        if (!records) return next(Errors.NotFound);
        next(null, records);
      },
      function (records, next) {
        records.forEach(function (record) {
          updatedRecords.push(record._id);
        });

        // Update the records
        self.dbs[coll].update(query.criteria.where, {
          '$set': values
        }, {
          multi: true
        }, next);
      },
      function (numReplaced, next) {
        if (numReplaced != updatedRecords.length) {
          return next(new Error('Could not update all records matching criteria.'));
        }

        self.dbs[coll].find({
          _id: {
            '$in': updatedRecords
          }
        }).sort({
          __ctr: 1
        }).exec(next);
      }
    ], function (err, records) {
      if (err) return cb(err);
      cb(null, utils.rewriteIds(records));
    });
  },

  destroy: function (coll, criteria, cb) {
    var self = this,
      query, deletedRecords;

    // Catch errors build query and return to the callback
    try {
      query = new Query(criteria);
    } catch (err) {
      return cb(err);
    }

    //Find ids of all documents matching delete criteria
    self.dbs[coll].find(query.criteria.where, function (err, records) {
      if (err) return cb(err);

      deletedRecords = records;
    });

    self.dbs[coll].remove(query.criteria.where, {
      multi: true
    }, function (err, numRemoved) {
      if (err) return cb(err);

      if (numRemoved == deletedRecords.length) {
        cb(null, utils.rewriteIds(deletedRecords));
      } else {
        cb(new Error('Could not delete all records matching criteria.'));
      }
    });
  }
});<commit_msg>Fixed missing comma.<commit_after>var base = require('base-framework'),
  _ = require('lodash'),
  Datastore = require('nedb'),
  path = require('path'),
  rimraf = require('rimraf'),
  async = require('async'),
  mkdirp = require('mkdirp'),
  Query = require('./query'),
  Document = require('./document'),
  utils = require('./utils'),
  Errors = require('waterline-errors').adapter;

module.exports = base.createChild().addInstanceMethods({
  init: function (config, collections) {
    this.config = config || {};
    this.dbRoot = path.join(this.config.filePath, this.config.identity);
    this.collections = collections || {};
    this.dbs = {};

    return this;
  },

  initConn: function (cb) {
    var self = this;
    mkdirp(self.dbRoot, function (err) {
      if (err) {
        return cb(err);
      }
      async.each(_.keys(self.collections), function (key, nextCollection) {
        self.registerCollection(self.collections[key], nextCollection);
      }, cb);
    });
  },

  registerCollection: function (collection, cb) {
    this.collections[collection.identity] = collection;
    this.loadDB(collection.identity, cb);
  },

  loadDB: function (collectionID, cb) {
    var self = this,
      collection = self.collections[collectionID];

    if (!self.dbs[collectionID]) {
      self.dbs[collectionID] = new Datastore({
        filename: path.join(self.dbRoot, collectionID + '.nedb')
      });
      self.dbs[collectionID].loadDatabase(_.bind(function (err) {
        if (err) {
          return cb(err);
        }

        var dbself = this,
          def = _.clone(collection.definition);

        async.waterfall([

          function (next) {
            // Loop through the def and process attributes for each key
            var keys = _.keys(def);
            async.each(keys, _processKey, next);

            function _processKey(key, cb) {
              if (key == 'id') {
                return cb();
              }

              if (def[key].autoIncrement) {
                delete def[key].autoIncrement;
              }

              if (def[key].unique || def[key].index) {
                return dbself.ensureIndex({
                  fieldName: key,
                  sparse: true,
                  unique: def[key].unique
                }, function (err) {
                  if (err) return cb(err);
                  def[key].indexed = true;
                  cb();
                });
              }

              cb();
            }
          },
          function (next) {
            dbself.ensureIndex({
              fieldName: '__ctr',
              unique: true
            }, next);
          },
          function (next) {
            var group = {
              reduce: function (curr, result) {
                if (curr.__ctr > result.ctr) {
                  result.ctr = curr.__ctr;
                }
              },
              initial: {
                ctr: 0
              }
            };
            dbself.find({}).group(group).exec(next);
          },
          function (res, next) {
            dbself.ctr = res[0].ctr;
            self.collections[collectionID].schema = def;
            next(null, def);
          }
        ], cb);
      }, self.dbs[collectionID]));
    }
  },

  describe: function (coll, cb) {
    var des = null;
    if (!_.isEmpty(this.collections[coll]) && !_.isEmpty(this.collections[coll].schema)) {
      des = this.collections[coll].schema;
    }

    return cb(null, des);
  },

  createCollection: function (coll, definition, cb) {
    this.registerCollection({
      definition: definition,
      identity: coll
    }, cb);
  },

  dropCollection: function (coll, relations, cb) {
    var self = this;
    db = self.dbs[coll];
    db.remove({}, {
      multi: true
    }, function (err, numRemoved) {
      if (err) {
        return cb(err);
      }
      delete self.dbs[coll];
      delete self.collections[coll];
      rimraf(db.filename, cb);
    });
  },

  select: function (coll, criteria, cb) {
    var query;

    // Catch errors from building query and return to the callback
    try {
      query = new Query(criteria);
    } catch (err) {
      return cb(err);
    }

    var where = query.criteria.where || {};
    var queryOptions = _.omit(query.criteria, 'where');

    // Run Normal Query on collection
    var cursor = this.dbs[coll].find(where);

    if (query.aggregate) {
      cursor.group(query.aggregateGroup);
    }

    if (queryOptions.sort) {
      cursor.sort(queryOptions.sort);
    } else {
      cursor.sort({
        __ctr: 1
      });
    }

    if (queryOptions.skip) {
      cursor.skip(queryOptions.skip);
    }
    if (queryOptions.limit) {
      cursor.limit(queryOptions.limit);
    }

    cursor.exec(function (err, docs) {
      cb(err, utils.rewriteIds(docs));
    });

  },

  insert: function (coll, data, cb) {
    var db = this.dbs[coll];

    delete data.id;
    delete data._id;
    data.__ctr = ++db.ctr;

    db.insert(data, function (err, result) {
      if (err) return cb(err);

      result.id = result._id;
      delete result._id;
      delete result.__ctr;
      cb(err, result);
    });
  },

  insertEach: function (coll, values, cb) {
    var db = this.dbs[coll];

    _.each(values, function (data) {
      delete data.id;
      delete data._id;
      data.__ctr = ++db.ctr;
    });

    db.insert(values, function (err, result) {
      if (err) return cb(err);

      cb(err, utils.rewriteIds(result));
    });
  },

  update: function (coll, criteria, values, cb) {
    var self = this,
      query;

    // Catch errors build query and return to the callback
    try {
      query = new Query(criteria);
    } catch (err) {
      return cb(err);
    }

    values = new Document(values, this.collections[coll].schema).values;

    // NEDB doesn't allow ID's to be updated
    if (values.id) delete values.id;
    if (values._id) delete values._id;

    var updatedRecords = [];
    async.waterfall([
      // Lookup records being updated and grab their ID's
      // Useful for later looking up the record after an insert
      // Required because options may not contain an ID
      function (next) {
        self.dbs[coll].find(query.criteria.where, next);
      },
      function (records, next) {
        if (!records) return next(Errors.NotFound);
        next(null, records);
      },
      function (records, next) {
        records.forEach(function (record) {
          updatedRecords.push(record._id);
        });

        // Update the records
        self.dbs[coll].update(query.criteria.where, {
          '$set': values
        }, {
          multi: true
        }, next);
      },
      function (numReplaced, next) {
        if (numReplaced != updatedRecords.length) {
          return next(new Error('Could not update all records matching criteria.'));
        }

        self.dbs[coll].find({
          _id: {
            '$in': updatedRecords
          }
        }).sort({
          __ctr: 1
        }).exec(next);
      }
    ], function (err, records) {
      if (err) return cb(err);
      cb(null, utils.rewriteIds(records));
    });
  },

  destroy: function (coll, criteria, cb) {
    var self = this,
      query, deletedRecords;

    // Catch errors build query and return to the callback
    try {
      query = new Query(criteria);
    } catch (err) {
      return cb(err);
    }

    //Find ids of all documents matching delete criteria
    self.dbs[coll].find(query.criteria.where, function (err, records) {
      if (err) return cb(err);

      deletedRecords = records;
    });

    self.dbs[coll].remove(query.criteria.where, {
      multi: true
    }, function (err, numRemoved) {
      if (err) return cb(err);

      if (numRemoved == deletedRecords.length) {
        cb(null, utils.rewriteIds(deletedRecords));
      } else {
        cb(new Error('Could not delete all records matching criteria.'));
      }
    });
  }
});<|endoftext|>


<commit_before>var authConfig = require('../config/authConfig.js');
var yelp = require('./yelpApi.js');
var Heap = require('heap');
var utils = require('./utils.js');
var fs = require('fs');
/*
Creates an instance of Recommendation Session

@constructor
@this {RecSession}
@param {string} loc The user-input location (zip code, address, etc).
@param {string} uid The randomly generated UID for this session.
@param {number} numRecs number of recommendations to return.
*/

var RecSession = function(loc, uid, numRecs) {
  this.location = loc;
  this.uniqueID = uid;
  this.recommendations = {};
  this.vetoes = {};
  this.numRecs = numRecs;
};

/*
Vetoes a restaurant or food category, removing it from the recommendations.

@param {string} foorOrRest Set to either "food" or "rest", this specifies what to veto.
@param {string} value The name of the food or restaurant to veto.
*/

RecSession.prototype.veto = function(foodOrRest, value) {
  if (foodOrRestaurant === 'food') {
    console.log('vetoing food: ', value);
  } else if (foodOrRestaurant === 'rest') {
    console.log('vetoing restaurant: ', value);
  } else {
    console.error('error: invalid argument');
  }
};

/*
Gets the restaurant data from Yelp and saves it in the instance of RecSession.

@this {RecSession}
@param {callback} cb The callback to invoke with (err, data, res)
*/

RecSession.prototype.getYelpData = function(cb) {
  var yelpClient = new yelp.YelpClient(authConfig);
  var searchParams = {
    sort: 1,
    radius_filter: 1000,
    location: this.location
  };

  yelpClient.searchRestaurants(searchParams, function(err, data, res) {
    if (err) {
      console.error('Error - Yelp API returned: ', err);
    }

    this.yelpData = data;
    cb(err, data, res);
  }.bind(this));
};
/*
Builds the recommendation queue.

@param {callback} cb The callback to invoke with (recommendations)
*/
RecSession.prototype.buildRecommendation = function(cb){
  var subCallback = function(err, data, res){
    if(!err){
      fs.appendFileSync('logs.txt', JSON.stringify(data))

      this.lat = data.region.center.latitude;
      this.longi = data.region.center.longitude;
      this.recQueue = data.businesses;
      this.recQueue.sort(function(a, b){
        return utils.calculateScore(this.lat, this.longi, a) - utils.calculateScore(this.lat, this.longi, b);
      }.bind(this));

      for(var i=0;i<this.numRecs && this.recQueue.length > 0;i++){
        var current = this.recQueue.pop();
        if(!this.vetoes[current.url]){
          this.recommendations[current.url] = current;
        }else{
          i --;
        }
      }

      cb(this.recommendations);
    }
  };

  this.getYelpData(subCallback.bind(this));
};

RecSession.prototype.getRecs = function(){
  return this.recommendations;
};

module.exports.RecSession = RecSession;
// (location, uid, numRecommendations)
// var testsesh = new RecSession('94103', 0, 3);
// testsesh.buildRecommendation(function(recs){
//   console.log(Object.keys(recs))
// })
<commit_msg>Added ability to ignore vetoed categories<commit_after>var authConfig = require('../config/authConfig.js');
var yelp = require('./yelpApi.js');
var Heap = require('heap');
var utils = require('./utils.js');
var fs = require('fs');
/*
Creates an instance of Recommendation Session

@constructor
@this {RecSession}
@param {string} loc The user-input location (zip code, address, etc).
@param {string} uid The randomly generated UID for this session.
@param {number} numRecs number of recommendations to return.
*/

var RecSession = function(loc, uid, numRecs) {
  this.location = loc;
  this.uniqueID = uid;
  this.recommendations = {};
  this.vetoes = {};
  this.numRecs = numRecs;
};

/*
Vetoes a restaurant or food category, removing it from the recommendations.

@param {string} foorOrRest Set to either "food" or "rest", this specifies what to veto.
@param {string} value The name of the food or restaurant to veto.
*/

RecSession.prototype.veto = function(foodOrRest, value) {
  if (foodOrRestaurant === 'food') {
    console.log('vetoing food: ', value);
  } else if (foodOrRestaurant === 'rest') {
    console.log('vetoing restaurant: ', value);
  } else {
    console.error('error: invalid argument');
  }
};

/*
Gets the restaurant data from Yelp and saves it in the instance of RecSession.

@this {RecSession}
@param {callback} cb The callback to invoke with (err, data, res)
*/

RecSession.prototype.getYelpData = function(cb) {
  var yelpClient = new yelp.YelpClient(authConfig);
  var searchParams = {
    sort: 1,
    radius_filter: 1000,
    location: this.location
  };

  yelpClient.searchRestaurants(searchParams, function(err, data, res) {
    if (err) {
      console.error('Error - Yelp API returned: ', err);
    }

    this.yelpData = data;
    cb(err, data, res);
  }.bind(this));
};
/*
Builds the recommendation queue.

@param {callback} cb The callback to invoke with (recommendations)
*/
RecSession.prototype.buildRecommendation = function(cb){
  var subCallback = function(err, data, res){
    if(!err){
      fs.appendFileSync('logs.txt', JSON.stringify(data))

      this.lat = data.region.center.latitude;
      this.longi = data.region.center.longitude;
      this.recQueue = data.businesses;
      this.recQueue.sort(function(a, b){
        return utils.calculateScore(this.lat, this.longi, a) - utils.calculateScore(this.lat, this.longi, b);
      }.bind(this));

      for(var i=0;i<this.numRecs && this.recQueue.length > 0;i++){
        var current = this.recQueue.pop();
        var vetoed = false;
        if(this.vetoes[current.url]){
          vetoed = true;
        };
        for(var j=0;j++;j<current.categories.length){
          if(this.vetoes[current.categories[i]]){
            vetoed = true;
          }
        };
        if(!vetoed){
          this.recommendations[current.url] = current;
        }else{
          i --;
        }
      }

      cb(this.recommendations);
    }
  };

  this.getYelpData(subCallback.bind(this));
};

RecSession.prototype.getRecs = function(){
  return this.recommendations;
};

RecSession.prototype.veto = function(command) {

};

module.exports.RecSession = RecSession;
// (location, uid, numRecommendations)
// var testsesh = new RecSession('94103', 0, 3);
// testsesh.buildRecommendation(function(recs){
//   console.log(Object.keys(recs))
// })
<|endoftext|>


<commit_before>// karma.conf.js
module.exports = function(config) {
  config.set({
    frameworks: ["jasmine"],
    files: [
      "node_modules/angular/angular.js",
      "node_modules/angular-mocks/angular-mocks.js",
      "http://localhost:5555/init.js",
      "http://localhost:5555/test_module/service.js",
      "http://localhost:5555/test_module/controller.js",

      "specs/*.js"
    ],
    exclude: [
      "specs/*_e2e_spec.js"
    ],
    logLevel: "LOG_DEBUG",
    browsers: ["PhantomJS"],
    autoWatch: false,
    singleRun: true
  });
};<commit_msg>Test fix<commit_after>// karma.conf.js
module.exports = function(config) {
  config.set({
    frameworks: ["jasmine"],
    files: [
      "node_modules/angular/angular.js",
      "node_modules/angular-mocks/angular-mocks.js",
      "http://localhost:5555/wutu.js",

      "specs/*.js"
    ],
    exclude: [
      "specs/*_e2e_spec.js"
    ],
    logLevel: "LOG_DEBUG",
    browsers: ["PhantomJS"],
    autoWatch: false,
    singleRun: true
  });
};<|endoftext|>


<commit_before>var arduino = require('duino')

var events = require('events'),
    child  = require('child_process'),
    util   = require('util'),
    colors = require('colors'),
    serial = require('serialport')

var tryFirst;
// tryFirst = 'ttyACM0';

var detect = arduino.Board.prototype.detect = function (callback) {
  this.log('info', 'attempting to find Arduino board');
  var self = this;
  child.exec('ls /dev | grep usb', function(err, stdout, stderr){
    var usb = stdout.slice(0, -1).split('\n'),
        found = false,
        err = null,
        possible, temp;

    if(tryFirst) usb.push(tryFirst);

    while ( usb.length ) {
      possible = usb.pop();

      if (possible.slice(0, 2) !== 'cu') {
        try {
          self.log('info','trying /dev/' + possible)
          temp = new serial.SerialPort('/dev/' + possible, {
            baudrate: 115200,
            parser: serial.parsers.readline('\n')
          });
        } catch (e) {
          err = e;
        }
        if (!err) {
          found = temp;
          self.log('info', 'found board on device: ' + '/dev/' + possible);
          self.log('info', 'found board at port: ' + temp.port);
          break;
        } else {
          err = new Error('Could not find Arduino');
        }
      }
    }

    callback(err, found);
  });
}

exports.blink = function(){
	var board = new arduino.Board({
	  debug: true
	})

	var led = new arduino.Led({
	  board: board,
	  pin: 13
	})
	led.blink()
}

var Controller = function(){
	this.getSerialConnection()
}

Controller.prototype.getSerialConnection = function(){
	var self = this
	detect.call(this,function(err,serial){
		if(err){
			console.error('Failed to connect to board')
			console.error(err.message)
			return
		}
		self.serial = serial
		self.serial.on('data', function(data){
        	console.log('received: '+data)
        	// self.emit('data', data)
      	})
	})
}

Controller.prototype.log = console.log

Controller.prototype.servosOn = function(){
	this.board.write("!7100000.")
}

Controller.prototype.servosOff = function(){
	this.board.write("!8100000.")
}

Controller.prototype.servosStop = function(){
	this.board.write("!6100000.")
}

Controller.prototype.blink = function(){

	this.stopBlink()

	var led = this.led = new arduino.Led({
	  board: this.board,
	  pin: 13
	})

	this.blinker =  setInterval(function(){
	    if (led.bright) {
	      led.off()
	    } else {
	      led.on();
	    }
	}, 1000);

}

Controller.prototype.stopBlink = function(){
	if(this.blinker){
		clearInterval(this.blinker)
		this.blinker = null
	}
	this.led = null
}

Controller.prototype.setPosition = function(degrees){
	this.servo1.write(degrees)
}

Controller.prototype.getPosition = function(fn){
	this.servo1.once('read',fn)
	this.servo1.read()
}

exports.createController = function(){
	return new Controller();
}<commit_msg>repurpose duino, now test w/ pi<commit_after>var arduino = require('duino')

var events = require('events'),
    child  = require('child_process'),
    util   = require('util'),
    colors = require('colors'),
    serial = require('serialport')

var tryFirst;
tryFirst = 'ttyACM0';

var detect = arduino.Board.prototype.detect = function (callback) {
  this.log('info', 'attempting to find Arduino board');
  var self = this;
  child.exec('ls /dev | grep usb', function(err, stdout, stderr){
    var usb = stdout.slice(0, -1).split('\n'),
        found = false,
        err = null,
        possible, temp;

    if(tryFirst) usb.push(tryFirst);

    while ( usb.length ) {
      possible = usb.pop();

      if (possible.slice(0, 2) !== 'cu') {
        try {
          self.log('info','trying /dev/' + possible)
          temp = new serial.SerialPort('/dev/' + possible, {
            baudrate: 115200,
            parser: serial.parsers.readline('\n')
          });
        } catch (e) {
          err = e;
        }
        if (!err) {
          found = temp;
          self.log('info', 'found board on device: ' + '/dev/' + possible);
          self.log('info', 'found board at port: ' + temp.port);
          break;
        } else {
          err = new Error('Could not find Arduino');
        }
      }
    }

    callback(err, found);
  });
}

exports.blink = function(){
	var board = new arduino.Board({
	  debug: true
	})

	var led = new arduino.Led({
	  board: board,
	  pin: 13
	})
	led.blink()
}

var Controller = function(){
	this.getSerialConnection()
}

Controller.prototype.getSerialConnection = function(){
	var self = this
	detect.call(this,function(err,serial){
		if(err){
			console.error('Failed to connect to board')
			console.error(err.message)
			return
		}
		self.serial = serial
		self.serial.on('data', function(data){
        	console.log('received: '+data)
        	// self.emit('data', data)
      	})
	})
}

Controller.prototype.log = console.log

Controller.prototype.servosOn = function(){
	this.board.write("!7100000.")
}

Controller.prototype.servosOff = function(){
	this.board.write("!8100000.")
}

Controller.prototype.servosStop = function(){
	this.board.write("!6100000.")
}

Controller.prototype.blink = function(){

	this.stopBlink()

	var led = this.led = new arduino.Led({
	  board: this.board,
	  pin: 13
	})

	this.blinker =  setInterval(function(){
	    if (led.bright) {
	      led.off()
	    } else {
	      led.on();
	    }
	}, 1000);

}

Controller.prototype.stopBlink = function(){
	if(this.blinker){
		clearInterval(this.blinker)
		this.blinker = null
	}
	this.led = null
}

Controller.prototype.setPosition = function(degrees){
	this.servo1.write(degrees)
}

Controller.prototype.getPosition = function(fn){
	this.servo1.once('read',fn)
	this.servo1.read()
}

exports.createController = function(){
	return new Controller();
}<|endoftext|>


<commit_before>var authConfig = require('../config/authConfig.js');
var yelp = require('./yelpApi.js');
var utils = require('./utils.js');
var fs = require('fs');
/*
Creates an instance of Recommendation Session

@constructor
@this {RecSession}
@param {string} loc The user-input location (zip code, address, etc).
@param {string} uid The randomly generated UID for this session.
@param {number} numRecs number of recommendations to return.
*/

var RecSession = function(loc, uid, numRecs) {
  this.location = loc;
  this.uniqueID = uid;
  this.recommendations = {};
  this.vetoes = {};
  this.numRecs = numRecs;
  this.recQueue = [];
};

/*
Gets the restaurant data from Yelp and saves it in the instance of RecSession.

@this {RecSession}
@param {callback} cb The callback to invoke with (err, data, res)
*/

RecSession.prototype.getYelpData = function(cb) {
  var yelpClient = new yelp.YelpClient(authConfig);
  var searchParams = {
    sort: 1,
    radius_filter: 5000,
    location: this.location
  };

  yelpClient.searchRestaurants(searchParams, function(err, data, res) {
    if (err) {
      console.error('Error - Yelp API returned: ', err);
    }

    this.yelpData = data;
    cb(err, data, res);
  }.bind(this));
};
/*
Subroutine for building recommendations

@param {callback} cb The callback to invoke with (recommendations)
@param {string} err The error returned
@param {object} data The data returned from Yelp
@param {res} res The response from Yelp
*/
var subCallback = function(cb, err, data, res){
  if(!err){
    this.lat = data.region.center.latitude;
    this.longi = data.region.center.longitude;
    this.recQueue = data.businesses;
    this.recQueue.sort(function(a, b){
      return utils.calculateScore(a) - utils.calculateScore(b);
    }.bind(this));

    for(var i=0;i<this.numRecs && this.recQueue.length > 0;i++){
      var current = this.recQueue.pop();
      this.recommendations[current.id] = current;
    };

    cb(this.recommendations);
  }
};
/*
Builds the recommendation queue.

@param {callback} cb The callback to invoke with (recommendations)
*/
RecSession.prototype.buildRecommendation = function(cb){

  this.getYelpData(subCallback.bind(this, cb));
};
/*
Returns recs

@returns {object} Object of recommendations keyed by id.
*/
RecSession.prototype.getRecs = function(){
  return this.recommendations;
};
/*
Adds veto to session

@param {object} Takes command object of form {key: 'id'|'object', val: valueToVeto}
*/
RecSession.prototype.veto = function(command) {
  // Refactor using lodash later.
  this.vetoes[command.val] = true;
  if(command.key === 'id'){
    delete this.recommendations[command.val];
  }else{
    var keys = Object.keys(this.recommendations);
    for(var i=0;i<keys.length;i++){
      if(this.recommendations[keys[i]].categories[0].indexOf(command.val) > -1){
        delete this.recommendations[keys[i]];
      }
    }
  };
  for(var i=0;i<this.numRecs && this.recQueue.length > 0 && Object.keys(this.recommendations).length < this.numRecs;i++){
    var current = this.recQueue.pop();
    var vetoed = false;
    if(this.vetoes[current.id]){
      vetoed = true;
    };
    // First in array is food-related;
    for(var j=0;j++;j<current.categories[0].length){
      if(this.vetoes[current.categories[0][i]]){
        vetoed = true;
      }
    };
    if(!vetoed){
      this.recommendations[current.id] = current;
    }else{
      i --;
    }
  };

};
module.exports = RecSession;

// // (location, uid, numRecommendations)
// var testsesh = new RecSession('94103', 0, 3);
// testsesh.veto({key:'id', val:'crepes-a-go-go-san-francisco-2'})
// testsesh.buildRecommendation(function(recs){
//   console.log(Object.keys(recs))
//   testsesh.veto({key:'id', val:'so-san-francisco-4'})
//   console.log(Object.keys(testsesh.getRecs()));
//   testsesh.veto({key:'category', val:'asianfusion'})
//   console.log(Object.keys(testsesh.getRecs()));
// })
<commit_msg>changed subroutine name<commit_after>var authConfig = require('../config/authConfig.js');
var yelp = require('./yelpApi.js');
var utils = require('./utils.js');
var fs = require('fs');
/*
Creates an instance of Recommendation Session

@constructor
@this {RecSession}
@param {string} loc The user-input location (zip code, address, etc).
@param {string} uid The randomly generated UID for this session.
@param {number} numRecs number of recommendations to return.
*/

var RecSession = function(loc, uid, numRecs) {
  this.location = loc;
  this.uniqueID = uid;
  this.recommendations = {};
  this.vetoes = {};
  this.numRecs = numRecs;
  this.recQueue = [];
};

/*
Gets the restaurant data from Yelp and saves it in the instance of RecSession.

@this {RecSession}
@param {callback} cb The callback to invoke with (err, data, res)
*/

RecSession.prototype.getYelpData = function(cb) {
  var yelpClient = new yelp.YelpClient(authConfig);
  var searchParams = {
    sort: 1,
    radius_filter: 5000,
    location: this.location
  };

  yelpClient.searchRestaurants(searchParams, function(err, data, res) {
    if (err) {
      console.error('Error - Yelp API returned: ', err);
    }

    this.yelpData = data;
    cb(err, data, res);
  }.bind(this));
};
/*
Subroutine for building recommendations

@param {callback} cb The callback to invoke with (recommendations)
@param {string} err The error returned
@param {object} data The data returned from Yelp
@param {res} res The response from Yelp
*/
var gatherRecs = function(cb, err, data, res){
  if(!err){
    this.lat = data.region.center.latitude;
    this.longi = data.region.center.longitude;
    this.recQueue = data.businesses;
    this.recQueue.sort(function(a, b){
      return utils.calculateScore(a) - utils.calculateScore(b);
    }.bind(this));

    for(var i=0;i<this.numRecs && this.recQueue.length > 0;i++){
      var current = this.recQueue.pop();
      this.recommendations[current.id] = current;
    };

    cb(this.recommendations);
  }
};
/*
Builds the recommendation queue.

@param {callback} cb The callback to invoke with (recommendations)
*/
RecSession.prototype.buildRecommendation = function(cb){

  this.getYelpData(gatherRecs.bind(this, cb));
};
/*
Returns recs

@returns {object} Object of recommendations keyed by id.
*/
RecSession.prototype.getRecs = function(){
  return this.recommendations;
};
/*
Adds veto to session

@param {object} Takes command object of form {key: 'id'|'object', val: valueToVeto}
*/
RecSession.prototype.veto = function(command) {
  // Refactor using lodash later.
  this.vetoes[command.val] = true;
  if(command.key === 'id'){
    delete this.recommendations[command.val];
  }else{
    var keys = Object.keys(this.recommendations);
    for(var i=0;i<keys.length;i++){
      if(this.recommendations[keys[i]].categories[0].indexOf(command.val) > -1){
        delete this.recommendations[keys[i]];
      }
    }
  };
  for(var i=0;i<this.numRecs && this.recQueue.length > 0 && Object.keys(this.recommendations).length < this.numRecs;i++){
    var current = this.recQueue.pop();
    var vetoed = false;
    if(this.vetoes[current.id]){
      vetoed = true;
    };
    // First in array is food-related;
    for(var j=0;j++;j<current.categories[0].length){
      if(this.vetoes[current.categories[0][i]]){
        vetoed = true;
      }
    };
    if(!vetoed){
      this.recommendations[current.id] = current;
    }else{
      i --;
    }
  };

};
module.exports = RecSession;

// // (location, uid, numRecommendations)
// var testsesh = new RecSession('94103', 0, 3);
// testsesh.veto({key:'id', val:'crepes-a-go-go-san-francisco-2'})
// testsesh.buildRecommendation(function(recs){
//   console.log(Object.keys(recs))
//   testsesh.veto({key:'id', val:'so-san-francisco-4'})
//   console.log(Object.keys(testsesh.getRecs()));
//   testsesh.veto({key:'category', val:'asianfusion'})
//   console.log(Object.keys(testsesh.getRecs()));
// })
<|endoftext|>