<commit_before>/**
 * Provide a synchronous Collection API using fibers, backed by
 * MongoDB.  This is only for use on the server, and mostly identical
 * to the client API.
 *
 * NOTE: the public API methods must be run within a fiber. If you call
 * these outside of a fiber they will explode!
 */

var MongoDB = __meteor_bootstrap__.require('mongodb');
var Future = __meteor_bootstrap__.require('fibers/future');

// js2-mode AST blows up when parsing 'future.return()', so alias.
Future.prototype.ret = Future.prototype.return;

_Mongo = function (url) {
  var self = this;

  self.collection_queue = [];

  MongoDB.connect(url, function(err, db) {
    self.db = db;

    // drain queue of pending callbacks
    var c;
    while ((c = self.collection_queue.pop())) {
      Fiber(function () {
        db.collection(c.name, c.callback);
      }).run();
    }
  });
};

// protect against dangerous selectors.  falsey and {_id: falsey}
// are both likely programmer error, and not what you want,
// particularly for destructive operations.
_Mongo._rewriteSelector = function (selector) {
  // shorthand -- scalars match _id
  if ((typeof selector === 'string') || (typeof selector === 'number'))
    selector = {_id: selector};

  if (!selector || (('_id' in selector) && !selector._id))
    // can't match anything
    return {_id: Meteor.uuid()};
  else
    return selector;
};

// callback: lambda (err, collection) called when
// collection is ready to go, or on error.
_Mongo.prototype._withCollection = function(collection_name, callback) {
  var self = this;

  if (self.db) {
    self.db.collection(collection_name, callback);
  } else {
    self.collection_queue.push({name: collection_name, callback: callback});
  }
};

// This should be called synchronously with a write, to create a
// transaction on the current write fence, if any. After we can read
// the write, and after observers have been notified (or at least,
// after the observer notifiers have added themselves to the write
// fence), you should call 'committed()' on the object returned.
_Mongo.prototype._maybeBeginWrite = function () {
  var self = this;
  var fence = Meteor._CurrentWriteFence.get();
  if (fence)
    return fence.beginWrite();
  else
    return {committed: function () {}};
};

//////////// Public API //////////

// The write methods block until the database has confirmed the write
// (it may not be replicated or stable on disk, but one server has
// confirmed it.) (In the future we might have an option to turn this
// off, ie, to enqueue the request on the wire and return
// immediately.)  They return nothing on success, and raise an
// exception on failure.
//
// After making a write (with insert, update, remove), observers are
// notified asynchronously. If you want to receive a callback once all
// of the observer notifications have landed for your write, do the
// writes inside a write fence (set Meteor._CurrentWriteFence to a new
// _WriteFence, and then set a callback on the write fence.)
//
// Since our execution environment is single-threaded, this is
// well-defined -- a write "has been made" if it's returned, and an
// observer "has been notified" if its callback has returned.

_Mongo.prototype.insert = function (collection_name, document) {
  var self = this;

  if (collection_name === "___meteor_failure_test_collection" &&
      document.fail) {
    var e = new Error("Failure test");
    e.expected = true;
    throw e;
  }

  var write = self._maybeBeginWrite();

  var finish = Meteor.bindEnvironment(function () {
    Meteor.refresh({collection: collection_name});
    write.committed();
  }, function (e) {
    Meteor._debug("Exception while completing insert: " + e.stack);
  });

  var future = new Future;
  self._withCollection(collection_name, function (err, collection) {
    if (err) {
      future.ret(err);
      return;
    }

    collection.insert(document, {safe: true}, function (err) {
      if (err) {
        future.ret(err);
        return;
      }

      finish();
      future.ret();
    });
  });

  var err = future.wait();
  if (err)
    throw err;
};

_Mongo.prototype.remove = function (collection_name, selector) {
  var self = this;

  if (collection_name === "___meteor_failure_test_collection" &&
      selector.fail) {
    var e = new Error("Failure test");
    e.expected = true;
    throw e;
  }

  var write = self._maybeBeginWrite();

  var finish = Meteor.bindEnvironment(function () {
    Meteor.refresh({collection: collection_name});
    write.committed();
  }, function (e) {
    Meteor._debug("Exception while completing remove: " + e.stack);
  });

  // XXX does not allow options. matches the client.
  selector = _Mongo._rewriteSelector(selector);

  var future = new Future;
  self._withCollection(collection_name, function (err, collection) {
    if (err) {
      future.ret(err);
      return;
    }

    collection.remove(selector, {/* XXXsafe: true*/}, function (err) {
      if (err) {
        future.ret(err);
        return;
      }

      finish();
      future.ret();
    });
  });

  var err = future.wait();
  if (err)
    throw err;
};

_Mongo.prototype.update = function (collection_name, selector, mod, options) {
  var self = this;

  if (collection_name === "___meteor_failure_test_collection" &&
      selector.fail) {
    var e = new Error("Failure test");
    e.expected = true;
    throw e;
  }

  var write = self._maybeBeginWrite();

  var finish = Meteor.bindEnvironment(function () {
    Meteor.refresh({collection: collection_name});
    write.committed();
  }, function (e) {
    Meteor._debug("Exception while completing update: " + e.stack);
  });

  selector = _Mongo._rewriteSelector(selector);
  if (!options) options = {};

  var future = new Future;
  self._withCollection(collection_name, function (err, collection) {
    if (err) {
      future.ret(err);
      return;
    }

    var opts = {safe: true};
    // explictly enumerate options that minimongo supports
    if (options.upsert) opts.upsert = true;
    if (options.multi) opts.multi = true;

    collection.update(selector, mod, opts, function (err) {
      if (err) {
        future.ret(err);
        return;
      }

      finish();
      future.ret();
    });
  });

  var err = future.wait();
  if (err)
    throw err;
};

_Mongo.prototype.find = function (collection_name, selector, options) {
  var self = this;

  if (arguments.length === 1)
    selector = {};

  return _Mongo._makeCursor(self, collection_name, selector, options);
};

_Mongo.prototype.findOne = function (collection_name, selector, options) {
  var self = this;

  if (arguments.length === 1)
    selector = {};

  return self.find(collection_name, selector, options).fetch()[0];
};

// Cursors

// Returns a _Mongo.Cursor, or throws an exception on
// failure. Creating a cursor involves a database query, and we block
// until it returns.
_Mongo._makeCursor = function (mongo, collection_name, selector, options) {
  var future = new Future;

  options = options || {};
  selector = _Mongo._rewriteSelector(selector);

  mongo._withCollection(collection_name, function (err, collection) {
    if (err) {
      future.ret([false, err]);
      return;
    }
    var cursor = collection.find(selector, options.fields, {
      sort: options.sort, limit: options.limit, skip: options.skip});
    future.ret([true, cursor]);
  });

  var result = future.wait();
  if (!(result[0]))
    throw result[1];

  return new _Mongo.Cursor(mongo, collection_name, selector, options,
                           result[1]);
};

// Do not call directly. Use _Mongo._makeCursor instead.
_Mongo.Cursor = function (mongo, collection_name, selector, options, cursor) {
  var self = this;

  if (!cursor)
    throw new Error("Cursor required");

  // NB: 'options' and 'selector' have already been preprocessed by _makeCursor
  self.mongo = mongo;
  self.collection_name = collection_name;
  self.selector = selector;
  self.options = options;
  self.cursor = cursor;

  self.visited_ids = {};
};

_Mongo.Cursor.prototype.forEach = function (callback) {
  var self = this;
  var future = new Future;

  callback = Meteor.bindEnvironment(callback, function (e) {
    // XXX improve error message (and how we report it)
    Meteor._debug("Exception while running Meteor.Collection forEach callback"
                  + e.stack);
  });

  self.cursor.each(function (err, doc) {
    if (err || !doc || !doc._id) {
      future.ret(err);
    } else if (self.visited_ids[doc._id]) {
      // already seen this doc;  Mongo cursors can
      // return duplicates
    } else {
      self.visited_ids[doc._id] = true;
      callback(doc);
    }
  });

  var err = future.wait();
  if (err)
    throw err;
};

_Mongo.Cursor.prototype.map = function (callback) {
  var self = this;
  var res = [];
  self.forEach(function (doc) {
    res.push(callback(doc));
  });
  return res;
};

_Mongo.Cursor.prototype.rewind = function () {
  var self = this;

  // known to be synchronous
  self.cursor.rewind();

  self.visited_ids = {};
};

_Mongo.Cursor.prototype.fetch = function () {
  var self = this;
  var future = new Future;

  self.cursor.toArray(function (err, res) {
    future.ret([err, res]);
  });

  var result = future.wait();
  if (result[0])
    throw result[0];
  // dedup
  return _.uniq(result[1], false, function(doc) {
    return doc._id; });
};

_Mongo.Cursor.prototype.count = function () {
  var self = this;
  var future = new Future;

  self.cursor.count(function (err, res) {
    future.ret([err, res]);
  });

  var result = future.wait();
  if (result[0])
    throw result[0];
  return result[1];
};

// options to contain:
//  * callbacks:
//    - added (object, before_index)
//    - changed (new_object, at_index)
//    - moved (object, old_index, new_index) - can only fire with changed()
//    - removed (old_object, at_index)
//
// attributes available on returned LiveResultsSet
//  * stop(): end updates

_Mongo.Cursor.prototype.observe = function (options) {
  return new _Mongo.LiveResultsSet(this, options);
};

_Mongo.LiveResultsSet = function (cursor, options) {
  var self = this;

  // copy my cursor, so that the observe can run independently from
  // some other use of the cursor.
  self.cursor = _Mongo._makeCursor(cursor.mongo,
                                   cursor.collection_name,
                                   cursor.selector,
                                   cursor.options);

  // expose collection name
  self.collection_name = cursor.collection_name;

  // previous results snapshot.  on each poll cycle, diffs against
  // results drives the callbacks.
  self.results = [];

  // state for polling
  self.dirty = false; // do we need polling?
  self.pending_writes = []; // people to notify when polling completes
  self.poll_running = false; // is polling in progress now?
  self.polling_suspended = false; // is polling temporarily suspended?

  // (each instance of the class needs to get a separate throttling
  // context -- we don't want to coalesce invocations of markDirty on
  // different instances!)
  self._markDirty = _.throttle(self._unthrottled_markDirty, 50 /* ms */);

  // listen for the invalidation messages that will trigger us to poll
  // the database for changes
  var keys = self.cursor.options.key || {collection: cursor.collection_name};
  if (!(keys instanceof Array))
    keys = [keys];
  self.crossbar_listeners = _.map(keys, function (key) {
    return Meteor._InvalidationCrossbar.listen(key,function (notification,
                                                             complete) {
      // When someone does a transaction that might affect us,
      // schedule a poll of the database. If that transaction happens
      // inside of a write fence, block the fence until we've polled
      // and notified observers.
      var fence = Meteor._CurrentWriteFence.get();
      if (fence)
        self.pending_writes.push(fence.beginWrite());
      self._markDirty();
      complete();
    });
  });

  // user callbacks
  self.added = options.added;
  self.changed = options.changed;
  self.moved = options.moved;
  self.removed = options.removed;

  // run the first _poll() cycle synchronously.
  self._doPoll();

  // every once and a while, poll even if we don't think we're dirty,
  // for eventual consistency with database writes from outside the
  // Meteor universe
  self.refreshTimer = Meteor.setInterval(_.bind(self._markDirty, this),
                                         10 * 1000 /* 10 seconds */);
};

_Mongo.LiveResultsSet.prototype._unthrottled_markDirty = function () {
  var self = this;

  self.dirty = true;
  if (self.polling_suspended)
    return; // don't poll when told not to
  if (self.poll_running)
    return; // only one instance can run at once. just tell it to re-cycle.
  self.poll_running = true;

  Fiber(function () {
    self.dirty = false;
    var writes_for_cycle = self.pending_writes;
    self.pending_writes = [];
    self._doPoll(); // could yield, and set self.dirty
    _.each(writes_for_cycle, function (w) {w.committed();});

    self.poll_running = false;
    if (self.dirty || self.pending_writes.length)
      // rerun ourselves, but through _.throttle
      self._markDirty();
  }).run();
};

// interface for tests to control when polling happens
_Mongo.LiveResultsSet.prototype._suspendPolling = function() {
  this.polling_suspended = true;
};
_Mongo.LiveResultsSet.prototype._resumePolling = function() {
  this.polling_suspended = false;
  this._unthrottled_markDirty(); // poll NOW, don't wait
};

_Mongo.LiveResultsSet.prototype._doPoll = function () {
  var self = this;

  // Get the new query results
  self.cursor.rewind();
  var new_results = self.cursor.fetch();
  var old_results = self.results;

  LocalCollection._diffQuery(old_results, new_results, self);
  self.results = new_results;

};

_Mongo.LiveResultsSet.prototype.stop = function () {
  var self = this;
  _.each(self.crossbar_listeners, function (l) { l.stop(); });
  Meteor.clearInterval(self.refreshTimer);
};

_.extend(Meteor, {
  _Mongo: _Mongo
});
<commit_msg>Fix "Cursor is closed" intermittent error.<commit_after>/**
 * Provide a synchronous Collection API using fibers, backed by
 * MongoDB.  This is only for use on the server, and mostly identical
 * to the client API.
 *
 * NOTE: the public API methods must be run within a fiber. If you call
 * these outside of a fiber they will explode!
 */

var MongoDB = __meteor_bootstrap__.require('mongodb');
var Future = __meteor_bootstrap__.require('fibers/future');

// js2-mode AST blows up when parsing 'future.return()', so alias.
Future.prototype.ret = Future.prototype.return;

_Mongo = function (url) {
  var self = this;

  self.collection_queue = [];

  MongoDB.connect(url, function(err, db) {
    self.db = db;

    // drain queue of pending callbacks
    var c;
    while ((c = self.collection_queue.pop())) {
      Fiber(function () {
        db.collection(c.name, c.callback);
      }).run();
    }
  });
};

// protect against dangerous selectors.  falsey and {_id: falsey}
// are both likely programmer error, and not what you want,
// particularly for destructive operations.
_Mongo._rewriteSelector = function (selector) {
  // shorthand -- scalars match _id
  if ((typeof selector === 'string') || (typeof selector === 'number'))
    selector = {_id: selector};

  if (!selector || (('_id' in selector) && !selector._id))
    // can't match anything
    return {_id: Meteor.uuid()};
  else
    return selector;
};

// callback: lambda (err, collection) called when
// collection is ready to go, or on error.
_Mongo.prototype._withCollection = function(collection_name, callback) {
  var self = this;

  if (self.db) {
    self.db.collection(collection_name, callback);
  } else {
    self.collection_queue.push({name: collection_name, callback: callback});
  }
};

// This should be called synchronously with a write, to create a
// transaction on the current write fence, if any. After we can read
// the write, and after observers have been notified (or at least,
// after the observer notifiers have added themselves to the write
// fence), you should call 'committed()' on the object returned.
_Mongo.prototype._maybeBeginWrite = function () {
  var self = this;
  var fence = Meteor._CurrentWriteFence.get();
  if (fence)
    return fence.beginWrite();
  else
    return {committed: function () {}};
};

//////////// Public API //////////

// The write methods block until the database has confirmed the write
// (it may not be replicated or stable on disk, but one server has
// confirmed it.) (In the future we might have an option to turn this
// off, ie, to enqueue the request on the wire and return
// immediately.)  They return nothing on success, and raise an
// exception on failure.
//
// After making a write (with insert, update, remove), observers are
// notified asynchronously. If you want to receive a callback once all
// of the observer notifications have landed for your write, do the
// writes inside a write fence (set Meteor._CurrentWriteFence to a new
// _WriteFence, and then set a callback on the write fence.)
//
// Since our execution environment is single-threaded, this is
// well-defined -- a write "has been made" if it's returned, and an
// observer "has been notified" if its callback has returned.

_Mongo.prototype.insert = function (collection_name, document) {
  var self = this;

  if (collection_name === "___meteor_failure_test_collection" &&
      document.fail) {
    var e = new Error("Failure test");
    e.expected = true;
    throw e;
  }

  var write = self._maybeBeginWrite();

  var finish = Meteor.bindEnvironment(function () {
    Meteor.refresh({collection: collection_name});
    write.committed();
  }, function (e) {
    Meteor._debug("Exception while completing insert: " + e.stack);
  });

  var future = new Future;
  self._withCollection(collection_name, function (err, collection) {
    if (err) {
      future.ret(err);
      return;
    }

    collection.insert(document, {safe: true}, function (err) {
      if (err) {
        future.ret(err);
        return;
      }

      finish();
      future.ret();
    });
  });

  var err = future.wait();
  if (err)
    throw err;
};

_Mongo.prototype.remove = function (collection_name, selector) {
  var self = this;

  if (collection_name === "___meteor_failure_test_collection" &&
      selector.fail) {
    var e = new Error("Failure test");
    e.expected = true;
    throw e;
  }

  var write = self._maybeBeginWrite();

  var finish = Meteor.bindEnvironment(function () {
    Meteor.refresh({collection: collection_name});
    write.committed();
  }, function (e) {
    Meteor._debug("Exception while completing remove: " + e.stack);
  });

  // XXX does not allow options. matches the client.
  selector = _Mongo._rewriteSelector(selector);

  var future = new Future;
  self._withCollection(collection_name, function (err, collection) {
    if (err) {
      future.ret(err);
      return;
    }

    collection.remove(selector, {/* XXXsafe: true*/}, function (err) {
      if (err) {
        future.ret(err);
        return;
      }

      finish();
      future.ret();
    });
  });

  var err = future.wait();
  if (err)
    throw err;
};

_Mongo.prototype.update = function (collection_name, selector, mod, options) {
  var self = this;

  if (collection_name === "___meteor_failure_test_collection" &&
      selector.fail) {
    var e = new Error("Failure test");
    e.expected = true;
    throw e;
  }

  var write = self._maybeBeginWrite();

  var finish = Meteor.bindEnvironment(function () {
    Meteor.refresh({collection: collection_name});
    write.committed();
  }, function (e) {
    Meteor._debug("Exception while completing update: " + e.stack);
  });

  selector = _Mongo._rewriteSelector(selector);
  if (!options) options = {};

  var future = new Future;
  self._withCollection(collection_name, function (err, collection) {
    if (err) {
      future.ret(err);
      return;
    }

    var opts = {safe: true};
    // explictly enumerate options that minimongo supports
    if (options.upsert) opts.upsert = true;
    if (options.multi) opts.multi = true;

    collection.update(selector, mod, opts, function (err) {
      if (err) {
        future.ret(err);
        return;
      }

      finish();
      future.ret();
    });
  });

  var err = future.wait();
  if (err)
    throw err;
};

_Mongo.prototype.find = function (collection_name, selector, options) {
  var self = this;

  if (arguments.length === 1)
    selector = {};

  return _Mongo._makeCursor(self, collection_name, selector, options);
};

_Mongo.prototype.findOne = function (collection_name, selector, options) {
  var self = this;

  if (arguments.length === 1)
    selector = {};

  return self.find(collection_name, selector, options).fetch()[0];
};

// Cursors

// Returns a _Mongo.Cursor, or throws an exception on
// failure. Creating a cursor involves a database query, and we block
// until it returns.
_Mongo._makeCursor = function (mongo, collection_name, selector, options) {
  var future = new Future;

  options = options || {};
  selector = _Mongo._rewriteSelector(selector);

  mongo._withCollection(collection_name, function (err, collection) {
    if (err) {
      future.ret([false, err]);
      return;
    }
    var cursor = collection.find(selector, options.fields, {
      sort: options.sort, limit: options.limit, skip: options.skip});
    future.ret([true, cursor]);
  });

  var result = future.wait();
  if (!(result[0]))
    throw result[1];

  return new _Mongo.Cursor(mongo, collection_name, selector, options,
                           result[1]);
};

// Do not call directly. Use _Mongo._makeCursor instead.
_Mongo.Cursor = function (mongo, collection_name, selector, options, cursor) {
  var self = this;

  if (!cursor)
    throw new Error("Cursor required");

  // NB: 'options' and 'selector' have already been preprocessed by _makeCursor
  self.mongo = mongo;
  self.collection_name = collection_name;
  self.selector = selector;
  self.options = options;
  self.cursor = cursor;

  self.visited_ids = {};
};

_Mongo.Cursor.prototype.forEach = function (callback) {
  var self = this;
  var future = new Future;

  callback = Meteor.bindEnvironment(callback, function (e) {
    // XXX improve error message (and how we report it)
    Meteor._debug("Exception while running Meteor.Collection forEach callback"
                  + e.stack);
  });

  self.cursor.each(function (err, doc) {
    if (err || !doc || !doc._id) {
      future.ret(err);
    } else if (self.visited_ids[doc._id]) {
      // already seen this doc;  Mongo cursors can
      // return duplicates
    } else {
      self.visited_ids[doc._id] = true;
      callback(doc);
    }
  });

  var err = future.wait();
  if (err)
    throw err;
};

_Mongo.Cursor.prototype.map = function (callback) {
  var self = this;
  var res = [];
  self.forEach(function (doc) {
    res.push(callback(doc));
  });
  return res;
};

_Mongo.Cursor.prototype.rewind = function () {
  var self = this;

  // known to be synchronous
  self.cursor.rewind();

  self.visited_ids = {};
};

_Mongo.Cursor.prototype.fetch = function () {
  var self = this;
  var future = new Future;

  self.cursor.toArray(function (err, res) {
    future.ret([err, res]);
  });

  var result = future.wait();
  if (result[0])
    throw result[0];
  // dedup
  return _.uniq(result[1], false, function(doc) {
    return doc._id; });
};

_Mongo.Cursor.prototype.count = function () {
  var self = this;
  var future = new Future;

  self.cursor.count(function (err, res) {
    future.ret([err, res]);
  });

  var result = future.wait();
  if (result[0])
    throw result[0];
  return result[1];
};

// options to contain:
//  * callbacks:
//    - added (object, before_index)
//    - changed (new_object, at_index)
//    - moved (object, old_index, new_index) - can only fire with changed()
//    - removed (old_object, at_index)
//
// attributes available on returned LiveResultsSet
//  * stop(): end updates

_Mongo.Cursor.prototype.observe = function (options) {
  return new _Mongo.LiveResultsSet(this, options);
};

_Mongo.LiveResultsSet = function (cursor, options) {
  var self = this;

  // copy my cursor, so that the observe can run independently from
  // some other use of the cursor.
  self.cursor = _Mongo._makeCursor(cursor.mongo,
                                   cursor.collection_name,
                                   cursor.selector,
                                   cursor.options);

  // expose collection name
  self.collection_name = cursor.collection_name;

  // previous results snapshot.  on each poll cycle, diffs against
  // results drives the callbacks.
  self.results = [];

  // state for polling
  self.dirty = false; // do we need polling?
  self.pending_writes = []; // people to notify when polling completes
  self.poll_running = false; // is polling in progress now?
  self.polling_suspended = false; // is polling temporarily suspended?

  // (each instance of the class needs to get a separate throttling
  // context -- we don't want to coalesce invocations of markDirty on
  // different instances!)
  self._markDirty = _.throttle(self._unthrottled_markDirty, 50 /* ms */);

  // listen for the invalidation messages that will trigger us to poll
  // the database for changes
  var keys = self.cursor.options.key || {collection: cursor.collection_name};
  if (!(keys instanceof Array))
    keys = [keys];
  self.crossbar_listeners = _.map(keys, function (key) {
    return Meteor._InvalidationCrossbar.listen(key,function (notification,
                                                             complete) {
      // When someone does a transaction that might affect us,
      // schedule a poll of the database. If that transaction happens
      // inside of a write fence, block the fence until we've polled
      // and notified observers.
      var fence = Meteor._CurrentWriteFence.get();
      if (fence)
        self.pending_writes.push(fence.beginWrite());
      self._markDirty();
      complete();
    });
  });

  // user callbacks
  self.added = options.added;
  self.changed = options.changed;
  self.moved = options.moved;
  self.removed = options.removed;

  // run the first _poll() cycle synchronously.
  self.poll_running = true;
  self._doPoll();
  self.poll_running = false;

  // every once and a while, poll even if we don't think we're dirty,
  // for eventual consistency with database writes from outside the
  // Meteor universe
  self.refreshTimer = Meteor.setInterval(_.bind(self._markDirty, this),
                                         10 * 1000 /* 10 seconds */);
};

_Mongo.LiveResultsSet.prototype._unthrottled_markDirty = function () {
  var self = this;

  self.dirty = true;
  if (self.polling_suspended)
    return; // don't poll when told not to
  if (self.poll_running)
    return; // only one instance can run at once. just tell it to re-cycle.
  self.poll_running = true;

  Fiber(function () {
    self.dirty = false;
    var writes_for_cycle = self.pending_writes;
    self.pending_writes = [];
    self._doPoll(); // could yield, and set self.dirty
    _.each(writes_for_cycle, function (w) {w.committed();});

    self.poll_running = false;
    if (self.dirty || self.pending_writes.length)
      // rerun ourselves, but through _.throttle
      self._markDirty();
  }).run();
};

// interface for tests to control when polling happens
_Mongo.LiveResultsSet.prototype._suspendPolling = function() {
  this.polling_suspended = true;
};
_Mongo.LiveResultsSet.prototype._resumePolling = function() {
  this.polling_suspended = false;
  this._unthrottled_markDirty(); // poll NOW, don't wait
};

_Mongo.LiveResultsSet.prototype._doPoll = function () {
  var self = this;

  // Get the new query results
  self.cursor.rewind();
  var new_results = self.cursor.fetch();
  var old_results = self.results;

  LocalCollection._diffQuery(old_results, new_results, self);
  self.results = new_results;

};

_Mongo.LiveResultsSet.prototype.stop = function () {
  var self = this;
  _.each(self.crossbar_listeners, function (l) { l.stop(); });
  Meteor.clearInterval(self.refreshTimer);
};

_.extend(Meteor, {
  _Mongo: _Mongo
});
<|endoftext|>


<commit_before>const bugsnagInFlight = require('@bugsnag/in-flight')
const BugsnagPluginBrowserSession = require('@bugsnag/plugin-browser-session')
const LambdaTimeoutApproaching = require('./lambda-timeout-approaching')

// JS timers use a signed 32 bit integer for the millisecond parameter. SAM's
// "local invoke" has a bug that means it exceeds this amount, resulting in
// warnings. See https://github.com/aws/aws-sam-cli/issues/2519
const MAX_TIMER_VALUE = Math.pow(2, 31) - 1

const BugsnagPluginAwsLambda = {
  name: 'awsLambda',

  load (client) {
    bugsnagInFlight.trackInFlight(client)
    client._loadPlugin(BugsnagPluginBrowserSession)

    // Reset the app duration between invocations, if the plugin is loaded
    const appDurationPlugin = client.getPlugin('appDuration')

    if (appDurationPlugin) {
      appDurationPlugin.reset()
    }

    // AWS add a default unhandledRejection listener that forcefully exits the
    // process. This breaks reporting of unhandled rejections, so we have to
    // remove all existing listeners and call them after we handle the rejection
    if (client._config.autoDetectErrors && client._config.enabledErrorTypes.unhandledRejections) {
      const listeners = process.listeners('unhandledRejection')
      process.removeAllListeners('unhandledRejection')

      // This relies on our unhandled rejection plugin adding its listener first
      // using process.prependListener, so we can call it first instead of AWS'
      process.on('unhandledRejection', async (reason, promise) => {
        for (const listener of listeners) {
          await listener.call(process, reason, promise)
        }
      })
    }

    return {
      createHandler ({ flushTimeoutMs = 2000, lambdaTimeoutNotifyMs = 1000 } = {}) {
        return wrapHandler.bind(null, client, flushTimeoutMs, lambdaTimeoutNotifyMs)
      }
    }
  }
}

function wrapHandler (client, flushTimeoutMs, lambdaTimeoutNotifyMs, handler) {
  let _handler = handler

  if (handler.length > 2) {
    // This is a handler expecting a 'callback' argument, so we convert
    // it to return a Promise so '_handler' always has the same API
    _handler = promisifyHandler(handler)
  }

  return async function (event, context) {
    let lambdaTimeout

    // Guard against the "getRemainingTimeInMillis" being missing. This should
    // never happen but could when unit testing
    if (typeof context.getRemainingTimeInMillis === 'function' &&
      lambdaTimeoutNotifyMs > 0
    ) {
      // Clamp the timeout value between 0 and MAX_TIMER_VALUE
      const timeoutMs = Math.min(
        Math.max(
          context.getRemainingTimeInMillis() - lambdaTimeoutNotifyMs,
          0
        ),
        MAX_TIMER_VALUE
      )

      lambdaTimeout = setTimeout(function () {
        const handledState = {
          severity: 'warning',
          unhandled: true,
          severityReason: { type: 'log' }
        }

        const event = client.Event.create(
          new LambdaTimeoutApproaching(context.getRemainingTimeInMillis()),
          true,
          handledState,
          'aws lambda plugin',
          0
        )

        event.context = context.functionName || 'Lambda timeout approaching'

        client._notify(event)
      }, timeoutMs)
    }

    client.addMetadata('AWS Lambda context', context)

    if (client._config.autoTrackSessions) {
      client.startSession()
    }

    try {
      return await _handler(event, context)
    } catch (err) {
      if (client._config.autoDetectErrors && client._config.enabledErrorTypes.unhandledExceptions) {
        const handledState = {
          severity: 'error',
          unhandled: true,
          severityReason: { type: 'unhandledException' }
        }

        const event = client.Event.create(err, true, handledState, 'aws lambda plugin', 1)

        client._notify(event)
      }

      throw err
    } finally {
      if (lambdaTimeout) {
        clearTimeout(lambdaTimeout)
      }

      try {
        await bugsnagInFlight.flush(flushTimeoutMs)
      } catch (err) {
        client._logger.error(`Delivery may be unsuccessful: ${err.message}`)
      }
    }
  }
}

// Convert a handler that uses callbacks to an async handler
function promisifyHandler (handler) {
  return function (event, context) {
    return new Promise(function (resolve, reject) {
      const result = handler(event, context, function (err, response) {
        if (err) {
          reject(err)
          return
        }

        resolve(response)
      })

      // Handle an edge case where the passed handler has the callback parameter
      // but actually returns a promise. In this case we need to resolve/reject
      // based on the returned promise instead of in the callback
      if (isPromise(result)) {
        result.then(resolve).catch(reject)
      }
    })
  }
}

function isPromise (value) {
  return (typeof value === 'object' || typeof value === 'function') &&
    typeof value.then === 'function' &&
    typeof value.catch === 'function'
}

module.exports = BugsnagPluginAwsLambda
<commit_msg>Simplify the MAX_TIMER_VALUE logic<commit_after>const bugsnagInFlight = require('@bugsnag/in-flight')
const BugsnagPluginBrowserSession = require('@bugsnag/plugin-browser-session')
const LambdaTimeoutApproaching = require('./lambda-timeout-approaching')

// JS timers use a signed 32 bit integer for the millisecond parameter. SAM's
// "local invoke" has a bug that means it exceeds this amount, resulting in
// warnings. See https://github.com/aws/aws-sam-cli/issues/2519
const MAX_TIMER_VALUE = Math.pow(2, 31) - 1

const BugsnagPluginAwsLambda = {
  name: 'awsLambda',

  load (client) {
    bugsnagInFlight.trackInFlight(client)
    client._loadPlugin(BugsnagPluginBrowserSession)

    // Reset the app duration between invocations, if the plugin is loaded
    const appDurationPlugin = client.getPlugin('appDuration')

    if (appDurationPlugin) {
      appDurationPlugin.reset()
    }

    // AWS add a default unhandledRejection listener that forcefully exits the
    // process. This breaks reporting of unhandled rejections, so we have to
    // remove all existing listeners and call them after we handle the rejection
    if (client._config.autoDetectErrors && client._config.enabledErrorTypes.unhandledRejections) {
      const listeners = process.listeners('unhandledRejection')
      process.removeAllListeners('unhandledRejection')

      // This relies on our unhandled rejection plugin adding its listener first
      // using process.prependListener, so we can call it first instead of AWS'
      process.on('unhandledRejection', async (reason, promise) => {
        for (const listener of listeners) {
          await listener.call(process, reason, promise)
        }
      })
    }

    return {
      createHandler ({ flushTimeoutMs = 2000, lambdaTimeoutNotifyMs = 1000 } = {}) {
        return wrapHandler.bind(null, client, flushTimeoutMs, lambdaTimeoutNotifyMs)
      }
    }
  }
}

function wrapHandler (client, flushTimeoutMs, lambdaTimeoutNotifyMs, handler) {
  let _handler = handler

  if (handler.length > 2) {
    // This is a handler expecting a 'callback' argument, so we convert
    // it to return a Promise so '_handler' always has the same API
    _handler = promisifyHandler(handler)
  }

  return async function (event, context) {
    let lambdaTimeout

    // Guard against the "getRemainingTimeInMillis" being missing. This should
    // never happen but could when unit testing
    if (typeof context.getRemainingTimeInMillis === 'function' &&
      lambdaTimeoutNotifyMs > 0 &&
      lambdaTimeoutNotifyMs <= MAX_TIMER_VALUE
    ) {
      const timeoutMs = context.getRemainingTimeInMillis() - lambdaTimeoutNotifyMs

      lambdaTimeout = setTimeout(function () {
        const handledState = {
          severity: 'warning',
          unhandled: true,
          severityReason: { type: 'log' }
        }

        const event = client.Event.create(
          new LambdaTimeoutApproaching(context.getRemainingTimeInMillis()),
          true,
          handledState,
          'aws lambda plugin',
          0
        )

        event.context = context.functionName || 'Lambda timeout approaching'

        client._notify(event)
      }, timeoutMs)
    }

    client.addMetadata('AWS Lambda context', context)

    if (client._config.autoTrackSessions) {
      client.startSession()
    }

    try {
      return await _handler(event, context)
    } catch (err) {
      if (client._config.autoDetectErrors && client._config.enabledErrorTypes.unhandledExceptions) {
        const handledState = {
          severity: 'error',
          unhandled: true,
          severityReason: { type: 'unhandledException' }
        }

        const event = client.Event.create(err, true, handledState, 'aws lambda plugin', 1)

        client._notify(event)
      }

      throw err
    } finally {
      if (lambdaTimeout) {
        clearTimeout(lambdaTimeout)
      }

      try {
        await bugsnagInFlight.flush(flushTimeoutMs)
      } catch (err) {
        client._logger.error(`Delivery may be unsuccessful: ${err.message}`)
      }
    }
  }
}

// Convert a handler that uses callbacks to an async handler
function promisifyHandler (handler) {
  return function (event, context) {
    return new Promise(function (resolve, reject) {
      const result = handler(event, context, function (err, response) {
        if (err) {
          reject(err)
          return
        }

        resolve(response)
      })

      // Handle an edge case where the passed handler has the callback parameter
      // but actually returns a promise. In this case we need to resolve/reject
      // based on the returned promise instead of in the callback
      if (isPromise(result)) {
        result.then(resolve).catch(reject)
      }
    })
  }
}

function isPromise (value) {
  return (typeof value === 'object' || typeof value === 'function') &&
    typeof value.then === 'function' &&
    typeof value.catch === 'function'
}

module.exports = BugsnagPluginAwsLambda
<|endoftext|>


<commit_before>import resourceReducer from './resource-reducer';
import actionTypes from './action-types';
import requestStatuses from './utils/request-statuses';
import setResourceMeta from './utils/set-resource-meta';
import upsertResources from './utils/upsert-resources';
import getStatus from './utils/get-status';
import getResources from './utils/get-resources';

export {
  resourceReducer,
  actionTypes,
  requestStatuses,
  setResourceMeta,
  upsertResources,
  getStatus,
  getResources
};
<commit_msg>Check for minified code that isn't NODE_ENV=production<commit_after>import resourceReducer from './resource-reducer';
import actionTypes from './action-types';
import requestStatuses from './utils/request-statuses';
import setResourceMeta from './utils/set-resource-meta';
import upsertResources from './utils/upsert-resources';
import getStatus from './utils/get-status';
import getResources from './utils/get-resources';

/*
* This is a dummy function to check if the function name has been altered by minification.
* If the function has been minified and NODE_ENV !== 'production', warn the user.
*/
function isCrushed() {}

if (
  process.env.NODE_ENV !== 'production' &&
  typeof isCrushed.name === 'string' &&
  isCrushed.name !== 'isCrushed'
) {
  console.error(
    'You are currently using minified code outside of NODE_ENV === \'production\'. ' +
    'This means that you are running a slower development build of Resourceful Redux. ' +
    'You can use loose-envify (https://github.com/zertosh/loose-envify) for browserify ' +
    'or DefinePlugin for webpack (http://stackoverflow.com/questions/30030031) ' +
    'to ensure you have the correct code for your production build.'
  );
}

export {
  resourceReducer,
  actionTypes,
  requestStatuses,
  setResourceMeta,
  upsertResources,
  getStatus,
  getResources
};
<|endoftext|>


<commit_before>
SpacebarsCompiler.parse = function (input) {

  var tree = HTMLTools.parseFragment(
    input,
    { getTemplateTag: TemplateTag.parseCompleteTag });

  return tree;
};

SpacebarsCompiler.compile = function (input, options) {
  var tree = SpacebarsCompiler.parse(input);
  return SpacebarsCompiler.codeGen(tree, options);
};

SpacebarsCompiler._TemplateTagReplacer = HTML.TransformingVisitor.extend();
SpacebarsCompiler._TemplateTagReplacer.def({
  visitObject: function (x) {
    if (x instanceof HTMLTools.TemplateTag) {

      // Make sure all TemplateTags in attributes have the right
      // `.position` set on them.  This is a bit of a hack
      // (we shouldn't be mutating that here), but it allows
      // cleaner codegen of "synthetic" attributes like TEXTAREA's
      // "value", where the template tags were originally not
      // in an attribute.
      if (this.inAttributeValue)
        x.position = HTMLTools.TEMPLATE_TAG_POSITION.IN_ATTRIBUTE;

      return this.codegen.codeGenTemplateTag(x);
    }

    return HTML.TransformingVisitor.prototype.visitObject.call(this, x);
  },
  visitAttributes: function (attrs) {
    if (attrs instanceof HTMLTools.TemplateTag)
      return this.codegen.codeGenTemplateTag(attrs);

    // call super (e.g. for case where `attrs` is an array)
    return HTML.TransformingVisitor.prototype.visitAttributes.call(this, attrs);
  },
  visitAttribute: function (name, value, tag) {
    this.inAttributeValue = true;
    var result = this.visit(value);
    this.inAttributeValue = false;

    if (result !== value) {
      // some template tags must have been replaced, because otherwise
      // we try to keep things `===` when transforming.  Wrap the code
      // in a function as per the rules.  You can't have
      // `{id: Blaze.View(...)}` as an attributes dict because the View
      // would be rendered more than once; you need to wrap it in a function
      // so that it's a different View each time.
      return BlazeTools.EmitCode(this.codegen.codeGenBlock(result));
    }
    return result;
  }
});

SpacebarsCompiler.codeGen = function (parseTree, options) {
  // is this a template, rather than a block passed to
  // a block helper, say
  var isTemplate = (options && options.isTemplate);
  var isBody = (options && options.isBody);

  var tree = parseTree;

  // The flags `isTemplate` and `isBody` are kind of a hack.
  if (isTemplate || isBody) {
    // optimizing fragments would require being smarter about whether we are
    // in a TEXTAREA, say.
    tree = SpacebarsCompiler.optimize(tree);
  }

  var codegen = new SpacebarsCompiler.CodeGen;
  tree = (new SpacebarsCompiler._TemplateTagReplacer(
    {codegen: codegen})).visit(tree);

  var code = '(function () { ';
  if (isTemplate || isBody) {
    code += 'var view = this; ';
  }
  code += 'return ';
  code += BlazeTools.toJS(tree);
  code += '; })';

  code = SpacebarsCompiler._beautify(code);

  return code;
};

SpacebarsCompiler._beautify = function (code) {
  if (Package.minifiers) {
    var result = Package.minifiers.UglifyJSMinify(
      code,
      { fromString: true,
        mangle: false,
        compress: false,
        output: { beautify: true,
                  indent_level: 2,
                  width: 80 } });
    var output = result.code;
    // Uglify interprets our expression as a statement and may add a semicolon.
    // Strip trailing semicolon.
    output = output.replace(/;$/, '');
    return output;
  } else {
    // don't actually beautify; no UglifyJS
    return code;
  }
};
<commit_msg>Don't try to uglify on the client<commit_after>
SpacebarsCompiler.parse = function (input) {

  var tree = HTMLTools.parseFragment(
    input,
    { getTemplateTag: TemplateTag.parseCompleteTag });

  return tree;
};

SpacebarsCompiler.compile = function (input, options) {
  var tree = SpacebarsCompiler.parse(input);
  return SpacebarsCompiler.codeGen(tree, options);
};

SpacebarsCompiler._TemplateTagReplacer = HTML.TransformingVisitor.extend();
SpacebarsCompiler._TemplateTagReplacer.def({
  visitObject: function (x) {
    if (x instanceof HTMLTools.TemplateTag) {

      // Make sure all TemplateTags in attributes have the right
      // `.position` set on them.  This is a bit of a hack
      // (we shouldn't be mutating that here), but it allows
      // cleaner codegen of "synthetic" attributes like TEXTAREA's
      // "value", where the template tags were originally not
      // in an attribute.
      if (this.inAttributeValue)
        x.position = HTMLTools.TEMPLATE_TAG_POSITION.IN_ATTRIBUTE;

      return this.codegen.codeGenTemplateTag(x);
    }

    return HTML.TransformingVisitor.prototype.visitObject.call(this, x);
  },
  visitAttributes: function (attrs) {
    if (attrs instanceof HTMLTools.TemplateTag)
      return this.codegen.codeGenTemplateTag(attrs);

    // call super (e.g. for case where `attrs` is an array)
    return HTML.TransformingVisitor.prototype.visitAttributes.call(this, attrs);
  },
  visitAttribute: function (name, value, tag) {
    this.inAttributeValue = true;
    var result = this.visit(value);
    this.inAttributeValue = false;

    if (result !== value) {
      // some template tags must have been replaced, because otherwise
      // we try to keep things `===` when transforming.  Wrap the code
      // in a function as per the rules.  You can't have
      // `{id: Blaze.View(...)}` as an attributes dict because the View
      // would be rendered more than once; you need to wrap it in a function
      // so that it's a different View each time.
      return BlazeTools.EmitCode(this.codegen.codeGenBlock(result));
    }
    return result;
  }
});

SpacebarsCompiler.codeGen = function (parseTree, options) {
  // is this a template, rather than a block passed to
  // a block helper, say
  var isTemplate = (options && options.isTemplate);
  var isBody = (options && options.isBody);

  var tree = parseTree;

  // The flags `isTemplate` and `isBody` are kind of a hack.
  if (isTemplate || isBody) {
    // optimizing fragments would require being smarter about whether we are
    // in a TEXTAREA, say.
    tree = SpacebarsCompiler.optimize(tree);
  }

  var codegen = new SpacebarsCompiler.CodeGen;
  tree = (new SpacebarsCompiler._TemplateTagReplacer(
    {codegen: codegen})).visit(tree);

  var code = '(function () { ';
  if (isTemplate || isBody) {
    code += 'var view = this; ';
  }
  code += 'return ';
  code += BlazeTools.toJS(tree);
  code += '; })';

  code = SpacebarsCompiler._beautify(code);

  return code;
};

SpacebarsCompiler._beautify = function (code) {
  if (Package.minifiers && Package.minifiers.UglifyJSMinify) {
    var result = Package.minifiers.UglifyJSMinify(
      code,
      { fromString: true,
        mangle: false,
        compress: false,
        output: { beautify: true,
                  indent_level: 2,
                  width: 80 } });
    var output = result.code;
    // Uglify interprets our expression as a statement and may add a semicolon.
    // Strip trailing semicolon.
    output = output.replace(/;$/, '');
    return output;
  } else {
    // don't actually beautify; no UglifyJS
    return code;
  }
};
<|endoftext|>


<commit_before>const fs = require("fs-extra");
const path = require("path");
const ghdownload = require('github-download');
const request = require('request');
const vcsurl = require('vcsurl');
const parseURL = require('url').parse;
const tmp = require('tmp');
const exec = require('child_process').exec;
const cwd = require('process').cwd();
const inquirer = require('inquirer');

const config = require('../config');

function checkDestination(destination) {
  return Promise.resolve()
    .then(() => {
      const contents = fs.readdirSync(destination);
      if (contents.length) {
        console.log("Something already exists in this folder...");
      }
  });
}

function verifyURL(url) {
  // Next let's see if the expected repository exists. If it doesn't, ghdownload
  // will fail spectacularly in a way we can't catch, so we have to do it ourselves.
  return new Promise(function(accept, reject) {

    var configURL = parseURL(
      vcsurl(url)
        .replace("github.com", "raw.githubusercontent.com")
        .replace(/#.*/, "") +
        "/master/truffle.js"
    );

    var options = {
      method: 'HEAD',
      uri: 'https://' + configURL.host + configURL.path
    };
    request(options, function(error, r) {
      if (error) {
        return reject(new Error(
          "Error making request to " + options.uri + ". Got error: " + error.message +
          ". Please check the format of the requested resource."
        ));
      } else if (r.statusCode == 404) {
        return reject(new Error("Truffle Box at URL " + url + " doesn't exist. If you believe this is an error, please contact Truffle support."));
      } else if (r.statusCode != 200) {
        return reject(new Error("Error connecting to github.com. Please check your internet connection and try again."));
      }
      accept();
    });
  });
}

function setupTempDirectory() {
  return new Promise(function(accept, reject) {
    tmp.dir({dir: cwd, unsafeCleanup: true}, function(err, dir, cleanupCallback) {
      if (err) return reject(err);

      accept(path.join(dir, "box"), cleanupCallback);
    });
  });
}

function fetchRepository(url, dir) {
  return new Promise(function(accept, reject) {
    // Download the package from github.
    ghdownload(url, dir)
      .on('err', function(err) {
        reject(err);
      })
      .on('end', function() {
        accept();
      });
  });
}

async function copyTempIntoDestination(tmpDir, destination) {
  const currentContent = fs.readdirSync(destination);
  if (currentContent.length > 1) {
    const tmpContent = fs.readdirSync(tmpDir);
    for (let file of tmpContent) {
      if (currentContent.includes(file)) {
        var overwriting = [
          {
            type: 'confirm',
            name: 'overwrite',
            message: `Overwrite ${file}?`,
            default: false
          }
        ]

        await inquirer.prompt(overwriting)
          .then(async answer => {
            if (answer.overwrite) {
              try {
                await fs.remove(file);
                await fs.copy(tmpDir+"/"+file, destination+"/"+file);
              } catch (err) {
                console.error(err);
              }
            }
          });
      } else {
        try {
          await fs.copy(tmpDir+"/"+file, destination+"/"+file);
        } catch (err) {
          console.error(err);
        }
      }
    }
  } else {
    try {
      await fs.copy(tmpDir, destination);
    } catch (err) {
      console.error(err);
    }
  }
}

function readBoxConfig(destination) {
  var possibleConfigs = [
    path.join(destination, "truffle-box.json"),
    path.join(destination, "truffle-init.json")
  ];

  var configPath = possibleConfigs.reduce(function(path, alt) {
    return path || fs.existsSync(alt) && alt;
  }, undefined);

  return config.read(configPath);
}

function cleanupUnpack(boxConfig, destination) {
  var needingRemoval = boxConfig.ignore || [];

  // remove box config file
  needingRemoval.push("truffle-box.json");
  needingRemoval.push("truffle-init.json");

  var promises = needingRemoval.map(function(file_path) {
    return path.join(destination, file_path);
  }).map(function(file_path) {
    return new Promise(function(accept, reject) {
      fs.remove(file_path, function(err) {
        if (err) return reject(err);
        accept();
      });
    });
  });

  return Promise.all(promises);
}

function installBoxDependencies(boxConfig, destination) {
  var postUnpack = boxConfig.hooks['post-unpack']

  return new Promise(function(accept, reject) {
    if (postUnpack.length === 0) {
      return accept();
    }

    exec(postUnpack, {cwd: destination}, function(err, stdout, stderr) {
      if (err) return reject(err);
      accept(stdout, stderr);
    });
  });
}

module.exports = {
  checkDestination: checkDestination,
  verifyURL: verifyURL,
  setupTempDirectory: setupTempDirectory,
  fetchRepository: fetchRepository,
  copyTempIntoDestination: copyTempIntoDestination,
  readBoxConfig: readBoxConfig,
  cleanupUnpack: cleanupUnpack,
  installBoxDependencies: installBoxDependencies
}
<commit_msg>async to sync fs methods & code cleanup<commit_after>const fs = require("fs-extra");
const path = require("path");
const ghdownload = require('github-download');
const request = require('request');
const vcsurl = require('vcsurl');
const parseURL = require('url').parse;
const tmp = require('tmp');
const exec = require('child_process').exec;
const cwd = require('process').cwd();
const inquirer = require('inquirer');

const config = require('../config');

function checkDestination(destination) {
  return Promise.resolve()
    .then(() => {
      const contents = fs.readdirSync(destination);
      if (contents.length) {
      }
  });
}

function verifyURL(url) {
  // Next let's see if the expected repository exists. If it doesn't, ghdownload
  // will fail spectacularly in a way we can't catch, so we have to do it ourselves.
  return new Promise(function(accept, reject) {

    var configURL = parseURL(
      vcsurl(url)
        .replace("github.com", "raw.githubusercontent.com")
        .replace(/#.*/, "") +
        "/master/truffle.js"
    );

    var options = {
      method: 'HEAD',
      uri: 'https://' + configURL.host + configURL.path
    };
    request(options, function(error, r) {
      if (error) {
        return reject(new Error(
          "Error making request to " + options.uri + ". Got error: " + error.message +
          ". Please check the format of the requested resource."
        ));
      } else if (r.statusCode == 404) {
        return reject(new Error("Truffle Box at URL " + url + " doesn't exist. If you believe this is an error, please contact Truffle support."));
      } else if (r.statusCode != 200) {
        return reject(new Error("Error connecting to github.com. Please check your internet connection and try again."));
      }
      accept();
    });
  });
}

function setupTempDirectory() {
  return new Promise(function(accept, reject) {
    tmp.dir({dir: cwd, unsafeCleanup: true}, function(err, dir, cleanupCallback) {
      if (err) return reject(err);

      accept(path.join(dir, "box"), cleanupCallback);
    });
  });
}

function fetchRepository(url, dir) {
  return new Promise(function(accept, reject) {
    // Download the package from github.
    ghdownload(url, dir)
      .on('err', function(err) {
        reject(err);
      })
      .on('end', function() {
        accept();
      });
  });
}

async function copyTempIntoDestination(tmpDir, destination) {
  const currentContent = fs.readdirSync(destination);
 //if (currentContent.length > 1) {
    const tmpContent = fs.readdirSync(tmpDir);
    for (let file of tmpContent) {
      if (currentContent.includes(file)) {
        console.log(`${file} already exists in this directory...`);
        var overwriting = [
          {
            type: 'confirm',
            name: 'overwrite',
            message: `Overwrite ${file}?`,
            default: false
          }
        ]

        await inquirer.prompt(overwriting)
          .then(answer => {
            if (answer.overwrite) {
              fs.removeSync(file);
              fs.copySync(`${tmpDir}/${file}`, `${destination}/${file}`);
            }
          });
      } else {
        fs.copySync(`${tmpDir}/${file}`, `${destination}/${file}`);
      }
    }
 /** }
  else {
    fs.copySync(tmpDir, destination);
  }**/
}

function readBoxConfig(destination) {
  var possibleConfigs = [
    path.join(destination, "truffle-box.json"),
    path.join(destination, "truffle-init.json")
  ];

  var configPath = possibleConfigs.reduce(function(path, alt) {
    return path || fs.existsSync(alt) && alt;
  }, undefined);

  return config.read(configPath);
}

function cleanupUnpack(boxConfig, destination) {
  var needingRemoval = boxConfig.ignore || [];

  // remove box config file
  needingRemoval.push("truffle-box.json");
  needingRemoval.push("truffle-init.json");

  var promises = needingRemoval.map(function(file_path) {
    return path.join(destination, file_path);
  }).map(function(file_path) {
    return new Promise(function(accept, reject) {
      fs.remove(file_path, function(err) {
        if (err) return reject(err);
        accept();
      });
    });
  });

  return Promise.all(promises);
}

function installBoxDependencies(boxConfig, destination) {
  var postUnpack = boxConfig.hooks['post-unpack']

  return new Promise(function(accept, reject) {
    if (postUnpack.length === 0) {
      return accept();
    }

    exec(postUnpack, {cwd: destination}, function(err, stdout, stderr) {
      if (err) return reject(err);
      accept(stdout, stderr);
    });
  });
}

module.exports = {
  checkDestination: checkDestination,
  verifyURL: verifyURL,
  setupTempDirectory: setupTempDirectory,
  fetchRepository: fetchRepository,
  copyTempIntoDestination: copyTempIntoDestination,
  readBoxConfig: readBoxConfig,
  cleanupUnpack: cleanupUnpack,
  installBoxDependencies: installBoxDependencies
}
<|endoftext|>


<commit_before>var async = require("async");
var fs = require("fs");
var mkdirp = require("mkdirp");
var path = require("path");
var solc = require("solc");
var path = require("path");
var Exec = require("./exec");
var Pudding = require("ether-pudding");
var PuddingGenerator = require("ether-pudding/generator");
var ConfigurationError = require("./errors/configurationerror");
var CompileError = require("./errors/compileerror");
var DeployError = require("./errors/deployerror");
var Graph = require("graphlib").Graph;
var isAcyclic = require("graphlib/lib/alg").isAcyclic;
var preOrder = require("graphlib/lib/alg").preorder;
var postOrder = require("graphlib/lib/alg").postorder;

var BlueBirdPromise = require('bluebird');

var Contracts = {
  resolve_headers(root) {
    root = path.resolve(root);
    var contract_name = path.basename(root).replace(/\.[^\.]*$/, "");

    var reduce_signature = function(signature) {
      return signature.reduce(function(previous, current, index) {
        var sig = "";
        if (index > 0) {
          sig += ", ";
        }
        sig += current.type;
        if (current.name != null && current.name != "") {
          sig += ` ${current.name}`;
        }
        return previous + sig;
      }, "");
    };

    var make_function = function(name, fn) {
      var returns = "";

      if (fn.outputs != null && fn.outputs.length > 0) {
        returns = ` returns (${reduce_signature(fn.outputs)})`;
      }

      return `  function ${name}(${reduce_signature(fn.inputs)})${returns}; \n`;
    };

    var code = this.resolve(root);

    var result = solc.compile(code, 1);

    if (result.errors != null) {
      throw new CompileError(result.errors.join(), root);
    }

    var compiled_contract = result.contracts[contract_name];
    var abi = JSON.parse(compiled_contract.interface);

    var headers = `contract ${contract_name} { \n`;

    for (var fn of abi) {
      switch(fn.type) {
        case "constructor":
          headers += make_function(contract_name, fn)
          break;
        case "function":
          headers += make_function(fn.name, fn);
          break;
        case "event":
          break;
        default:
          throw new Error(`Unknown type ${fn.type} found in ${root}`);
      }
    }

    headers += `} \n`;

    return headers;
  },

  resolve(root) {
    var imported = {};

    var import_file = (file) => {
      var code = fs.readFileSync(file, "utf-8");

      // Remove comments
      code = code.replace(/(\/\/.*(\n|$))/g, "");
      code = code.replace(/(\/\*(.|\n)*?\*\/)/g, "");
      code = code.replace("*/", ""); // Edge case.

      // Perform imports.
      code = code.replace(/import(_headers)? ('|")[^'"]+('|");/g, (match) => {
        match = match.replace(/'/g, '"');
        var import_name = match.split('"')[1];
        var import_path = path.dirname(file) + "/" + import_name + ".sol";

        // Don't import the same thing twice if there are two of the same dependency.
        if (imported[import_name] == true) {
          return "";
        }

        if (!fs.existsSync(import_path)) {
          throw `Could not find source for '${import_name} from ${file}'. Expected: ${import_path}`
        }

        imported[import_name] = true;

        if (match.indexOf("import_headers") == 0) {
          return this.resolve_headers(import_path) + "\n\n";
        } else {
          return import_file(import_path) + "\n\n";
        }
      });
      return code;
    };

    return import_file(root);
  },

  compile_all(config, callback) {
    var sources = {};
    var contracts = Object.keys(config.contracts.classes);
    for (var i = 0; i < contracts.length; i++) {
      var key = contracts[i];
      var contract = config.contracts.classes[key];
      var source = contract.source.replace("./contracts/", "");
      var full_path = path.resolve(config.working_dir, contract.source)
      sources[source] = fs.readFileSync(full_path, {encoding: "utf8"});
    }

    var result = solc.compile({sources: sources}, 1);

    if (result.errors != null) {
      callback(new CompileError(result.errors.join()));
      return;
    }

    var compiled_contract = result.contracts[key];

    for (var i = 0; i < contracts.length; i++) {
      var key = contracts[i];
      var contract = config.contracts.classes[key];
      var compiled_contract = result.contracts[key];
      contract["binary"] = compiled_contract.bytecode;
      contract["abi"] = JSON.parse(compiled_contract.interface);
    }

    callback();

    //   finished(null, contract);
    // }, callback);
  },

  write_contracts(config, description="contracts", callback) {
    var destination = config.contracts.build_directory;
    mkdirp(destination, function(err, result) {
      if (err != null) {
        callback(err);
        return;
      }

      var display_directory = "./" + path.join("./", destination.replace(config.working_dir, ""));
      if (config.argv.quietDeploy == null) {
        console.log(`Writing ${description} to ${display_directory}`);
      }

      PuddingGenerator.save(config.contracts.classes, destination, {removeExisting: true});

      callback();
    });
  },

  compile(config, callback) {
    async.series([
      (c) => {
        config.test_connection(function(error, coinbase) {
          if (error != null) {
            callback(new Error("Could not connect to your Ethereum client. Truffle uses your Ethereum client to compile contracts. Please ensure your client is running and can compile the all contracts within the contracts directory."));
          } else {
            c();
          }
        });
      },
      (c) => {
        this.compile_all(config, c);
      },
      (c) => {
        this.write_contracts(config, "contracts", c);
      }
    ], callback);
  },
  createContractAndWait(config, tx,contract_name) {

    return new Promise((accept, reject) => {

      config.web3.eth.sendTransaction(tx, (err, hash) => {
        if (err != null) {
          return reject(err);
        }

        var interval = setInterval(() => {

          config.web3.eth.getTransactionReceipt(hash, (err, receipt) => {
            if (err != null) {
              clearInterval(interval);

              return reject(err);
            }
            if (receipt != null) {
               console.log("Deployed: "+contract_name+" to address: "+receipt.contractAddress);

              accept({name:contract_name,address:receipt.contractAddress});
              clearInterval(interval);
            }
          });

        }, 1000);
      })
    });
  },
  build_dependency_graph(config,errorCallback)
  {
        console.log("Collecting dependencies...");
        //Iterate through all the contracts looking for libraries and building a dependency graph
        var dependsGraph = new Graph();
        for (var i = 0; i < config.app.resolved.deploy.length; i++) {
          var key = config.app.resolved.deploy[i];
          var contract_class = config.contracts.classes[key];

          if (contract_class == null) {
            errorCallback(new DeployError(`Error  could not find contract '${key}' for linking. Check truffle.json.`));
            return null;
          }
          //Add the contract to the depend graph
          dependsGraph.setNode(key);

          //Find references to any librarys
          //Library references are embedded in the bytecode of contracts with the format
          // "__Lib___________________________________" , where "Lib" is your library name and the whole
          // string is 40 characters long. This is the placeholder for the Lib's address.

          var regex = /__([^_]*)_*/g;
          var matches;
          while ( (matches = regex.exec(contract_class.binary)) !== null ) {
                var lib = matches[1];
                if(!dependsGraph.hasEdge(key,lib))
                   dependsGraph.setEdge(key, lib);
          }
        }
        //Check for cycles in the graph, the dependency graph needs to be a tree otherwise there's an error
        if(!isAcyclic(dependsGraph))
        {
           console.log("ERROR: Cycles in dependency graph");
           dependsGraph.edges().forEach(function(o){
              console.log(o.v+" -- depends on --> "+o.w);
           });
           errorCallback(new DeployError(`Error  linker found cyclic dependencies. Adjust your import statements to remove cycles.`));
           return null;
        }
        return dependsGraph;

  },
  link_dependencies(config,contract_data,contract_class,contract_name)
  {
    return (dependency_addresses) => {
          //All of the library dependencies to this contract have been deployed
          //Inject the address of each lib into this contract and then deploy it.
          dependency_addresses.forEach(function(lib){
            console.log("Linking Library: "+lib.name+" to contract: "+contract_name+" at address: "+lib.address);
            var bin_address = lib.address.replace("0x","");
            var re = new RegExp("__"+lib.name+"_*","g");
            contract_data.data = contract_data.data.replace(re,bin_address);
          });
          var contract = Pudding.whisk({
              abi: contract_class.abi,
              binary: contract_class.binary,
              contract_name: contract_name
            });
          return this.createContractAndWait(config,contract_data,contract_name);
    }
  },
  deploy(config, compile=true, done_deploying) {
    var coinbase = null;

    async.series([
      (c) => {
        config.web3.eth.getCoinbase(function(error, result) {
          coinbase = result;
          c(error, result);
        });
      },
      (c) => {
        if (compile == true) {
          this.compile_all(config, c);
        } else {
          c();
        }
      },
      (c) =>{
          Pudding.setWeb3(config.web3);
          var dependsGraph = this.build_dependency_graph(config,c);
          var dependsOrdering = postOrder(dependsGraph,dependsGraph.nodes());
          var deploy_promise = null;
          var contract_name ; //This is in global scope so that it can be used in the .catch below

          //Iterate over the dependency grpah in post order, deploy libraries first so we can
          //capture their addresses and use them to deploy the contracts that depend on them
          for(var i =0 ; i< dependsOrdering.length ;i++)
          {
              contract_name = dependsOrdering[i];
              var contract_class = config.contracts.classes[contract_name];

              if (contract_class == null) {
                c(new DeployError(`Could not find contract '${key}' for deployment. Check truffle.json.`));
                return;
              }

              var contract_data = { from: coinbase,
                                    gas: 3141592,
                                    //gasPrice: 50000000000, // 50 Shannon
                                    gasPrice: 100000000000, // 100 Shannon
                                    data: contract_class.binary
                                  };
              var dependencies = dependsGraph.successors(contract_name);

              //When we encounter a Library that is not dependant on other libraries, we can just
              //deploy it as normal
              if(dependencies.length == 0 )
              {
                 var contract = Pudding.whisk({
                  abi: contract_class.abi,
                  binary: contract_class.binary,
                  contract_name: contract_name
                });
                deploy_promise = this.createContractAndWait(config,contract_data,contract_name);

                //Store the promise in the graph so we can fetch it later
                dependsGraph.setNode(contract_name,deploy_promise);
              }
              //Contracts that have dependencies need to wait until those dependencies have been deployed
              //so we can inject the address into their byte code
              else
              {
                //Collect all the promises for the libraries this contract depends on into a list
                //NOTE: since this loop is traversing in post-order, we can be assured that this list
                //will contain ALL of the dependencies of this contract
                var depends_promises = dependencies.map(dependsGraph.node,dependsGraph);

                //Wait for all the dependencies to be committed and then do the linking step
                deploy_promise = Promise.all(depends_promises)
                                        .then(this.link_dependencies(config,contract_data,contract_class,contract_name));

                //It's possible that this contract is a dependency of some other contract so we store
                //it in the graph just in case
                dependsGraph.setNode(contract_name,deploy_promise);

              }
          }
          ///Now wait for all of the outstanding deployments to complete
          Promise.all(dependsGraph.nodes().map(dependsGraph.node,dependsGraph))
          .then(function(deployed_contracts) {

            deployed_contracts.forEach(function(a){config.contracts.classes[a.name].address = a.address;});

            c();
          }).catch(function(err) {
            c(new DeployError(err.message, contract_name));
          });

      },
    (c) => {
        this.write_contracts(config, "built contract files", c);
    },
    (c) => {
        this.after_deploy(config, c);
      }
    ], (err) => {
      if (err != null) {
        done_deploying(err);
        return;
      } else {
        done_deploying();
      }
    });
  },

  after_deploy(config, done) {
    async.eachSeries(config.app.resolved.after_deploy, function(file, iterator_callback) {
      if (config.argv.quietDeploy == null) {
        console.log(`Running post deploy script ${file}...`);
      }
      Exec.file(config, file, iterator_callback);
    }, done);
  }
}

module.exports = Contracts;
<commit_msg>Added nicer error handling when the deploy section of truffle.json has incorrect contract info<commit_after>var async = require("async");
var fs = require("fs");
var mkdirp = require("mkdirp");
var path = require("path");
var solc = require("solc");
var path = require("path");
var Exec = require("./exec");
var Pudding = require("ether-pudding");
var PuddingGenerator = require("ether-pudding/generator");
var ConfigurationError = require("./errors/configurationerror");
var CompileError = require("./errors/compileerror");
var DeployError = require("./errors/deployerror");
var Graph = require("graphlib").Graph;
var isAcyclic = require("graphlib/lib/alg").isAcyclic;
var preOrder = require("graphlib/lib/alg").preorder;
var postOrder = require("graphlib/lib/alg").postorder;

var BlueBirdPromise = require('bluebird');

var Contracts = {
  resolve_headers(root) {
    root = path.resolve(root);
    var contract_name = path.basename(root).replace(/\.[^\.]*$/, "");

    var reduce_signature = function(signature) {
      return signature.reduce(function(previous, current, index) {
        var sig = "";
        if (index > 0) {
          sig += ", ";
        }
        sig += current.type;
        if (current.name != null && current.name != "") {
          sig += ` ${current.name}`;
        }
        return previous + sig;
      }, "");
    };

    var make_function = function(name, fn) {
      var returns = "";

      if (fn.outputs != null && fn.outputs.length > 0) {
        returns = ` returns (${reduce_signature(fn.outputs)})`;
      }

      return `  function ${name}(${reduce_signature(fn.inputs)})${returns}; \n`;
    };

    var code = this.resolve(root);

    var result = solc.compile(code, 1);

    if (result.errors != null) {
      throw new CompileError(result.errors.join(), root);
    }

    var compiled_contract = result.contracts[contract_name];
    var abi = JSON.parse(compiled_contract.interface);

    var headers = `contract ${contract_name} { \n`;

    for (var fn of abi) {
      switch(fn.type) {
        case "constructor":
          headers += make_function(contract_name, fn)
          break;
        case "function":
          headers += make_function(fn.name, fn);
          break;
        case "event":
          break;
        default:
          throw new Error(`Unknown type ${fn.type} found in ${root}`);
      }
    }

    headers += `} \n`;

    return headers;
  },

  resolve(root) {
    var imported = {};

    var import_file = (file) => {
      var code = fs.readFileSync(file, "utf-8");

      // Remove comments
      code = code.replace(/(\/\/.*(\n|$))/g, "");
      code = code.replace(/(\/\*(.|\n)*?\*\/)/g, "");
      code = code.replace("*/", ""); // Edge case.

      // Perform imports.
      code = code.replace(/import(_headers)? ('|")[^'"]+('|");/g, (match) => {
        match = match.replace(/'/g, '"');
        var import_name = match.split('"')[1];
        var import_path = path.dirname(file) + "/" + import_name + ".sol";

        // Don't import the same thing twice if there are two of the same dependency.
        if (imported[import_name] == true) {
          return "";
        }

        if (!fs.existsSync(import_path)) {
          throw `Could not find source for '${import_name} from ${file}'. Expected: ${import_path}`
        }

        imported[import_name] = true;

        if (match.indexOf("import_headers") == 0) {
          return this.resolve_headers(import_path) + "\n\n";
        } else {
          return import_file(import_path) + "\n\n";
        }
      });
      return code;
    };

    return import_file(root);
  },

  compile_all(config, callback) {
    var sources = {};
    var contracts = Object.keys(config.contracts.classes);
    for (var i = 0; i < contracts.length; i++) {
      var key = contracts[i];
      var contract = config.contracts.classes[key];
      var source = contract.source.replace("./contracts/", "");
      var full_path = path.resolve(config.working_dir, contract.source)
      sources[source] = fs.readFileSync(full_path, {encoding: "utf8"});
    }

    var result = solc.compile({sources: sources}, 1);

    if (result.errors != null) {
      callback(new CompileError(result.errors.join()));
      return;
    }

    var compiled_contract = result.contracts[key];

    for (var i = 0; i < contracts.length; i++) {
      var key = contracts[i];
      var contract = config.contracts.classes[key];
      var compiled_contract = result.contracts[key];
      contract["binary"] = compiled_contract.bytecode;
      contract["abi"] = JSON.parse(compiled_contract.interface);
    }

    callback();

    //   finished(null, contract);
    // }, callback);
  },

  write_contracts(config, description="contracts", callback) {
    var destination = config.contracts.build_directory;
    mkdirp(destination, function(err, result) {
      if (err != null) {
        callback(err);
        return;
      }

      var display_directory = "./" + path.join("./", destination.replace(config.working_dir, ""));
      if (config.argv.quietDeploy == null) {
        console.log(`Writing ${description} to ${display_directory}`);
      }

      PuddingGenerator.save(config.contracts.classes, destination, {removeExisting: true});

      callback();
    });
  },

  compile(config, callback) {
    async.series([
      (c) => {
        config.test_connection(function(error, coinbase) {
          if (error != null) {
            callback(new Error("Could not connect to your Ethereum client. Truffle uses your Ethereum client to compile contracts. Please ensure your client is running and can compile the all contracts within the contracts directory."));
          } else {
            c();
          }
        });
      },
      (c) => {
        this.compile_all(config, c);
      },
      (c) => {
        this.write_contracts(config, "contracts", c);
      }
    ], callback);
  },
  createContractAndWait(config, tx,contract_name) {

    return new Promise((accept, reject) => {

      config.web3.eth.sendTransaction(tx, (err, hash) => {
        if (err != null) {
          return reject(err);
        }

        var interval = setInterval(() => {

          config.web3.eth.getTransactionReceipt(hash, (err, receipt) => {
            if (err != null) {
              clearInterval(interval);

              return reject(err);
            }
            if (receipt != null) {
               console.log("Deployed: "+contract_name+" to address: "+receipt.contractAddress);

              accept({name:contract_name,address:receipt.contractAddress});
              clearInterval(interval);
            }
          });

        }, 1000);
      })
    });
  },
  build_dependency_graph(config,errorCallback)
  {
        console.log("Collecting dependencies...");
        //Iterate through all the contracts looking for libraries and building a dependency graph
        var dependsGraph = new Graph();
        for (var i = 0; i < config.app.resolved.deploy.length; i++) {
          var key = config.app.resolved.deploy[i];
          var contract_class = config.contracts.classes[key];

          if (contract_class == null) {
            errorCallback(new DeployError(`Error  could not find contract '${key}' for linking. Check truffle.json.`));
            return null;
          }
          console.log("BINARY: "+contract_class.binary);

          if(contract_class.binary == null){
            errorCallback(new DeployError(`Error  could not find compiled binary for contract '${key}'. Check truffle.json.`));
            return null;
          }
          //Add the contract to the depend graph
          dependsGraph.setNode(key);

          //Find references to any librarys
          //Library references are embedded in the bytecode of contracts with the format
          // "__Lib___________________________________" , where "Lib" is your library name and the whole
          // string is 40 characters long. This is the placeholder for the Lib's address.

          var regex = /__([^_]*)_*/g;
          var matches;
          while ( (matches = regex.exec(contract_class.binary)) !== null ) {
                var lib = matches[1];
                if(!dependsGraph.hasEdge(key,lib))
                   dependsGraph.setEdge(key, lib);
          }
        }
        //Check for cycles in the graph, the dependency graph needs to be a tree otherwise there's an error
        if(!isAcyclic(dependsGraph))
        {
           console.log("ERROR: Cycles in dependency graph");
           dependsGraph.edges().forEach(function(o){
              console.log(o.v+" -- depends on --> "+o.w);
           });
           errorCallback(new DeployError(`Error  linker found cyclic dependencies. Adjust your import statements to remove cycles.`));
           return null;
        }
        return dependsGraph;

  },
  link_dependencies(config,contract_data,contract_class,contract_name)
  {
    return (dependency_addresses) => {
          //All of the library dependencies to this contract have been deployed
          //Inject the address of each lib into this contract and then deploy it.
          dependency_addresses.forEach(function(lib){
            console.log("Linking Library: "+lib.name+" to contract: "+contract_name+" at address: "+lib.address);
            var bin_address = lib.address.replace("0x","");
            var re = new RegExp("__"+lib.name+"_*","g");
            contract_data.data = contract_data.data.replace(re,bin_address);
          });
          var contract = Pudding.whisk({
              abi: contract_class.abi,
              binary: contract_class.binary,
              contract_name: contract_name
            });
          return this.createContractAndWait(config,contract_data,contract_name);
    }
  },
  deploy(config, compile=true, done_deploying) {
    var coinbase = null;

    async.series([
      (c) => {
        config.web3.eth.getCoinbase(function(error, result) {
          coinbase = result;
          c(error, result);
        });
      },
      (c) => {
        if (compile == true) {
          this.compile_all(config, c);

        } else {
          c();
        }
      },
      (c) =>{

          Pudding.setWeb3(config.web3);
          var dependsGraph = this.build_dependency_graph(config,c);
          if(dependsGraph == null)
             return;

          var dependsOrdering = postOrder(dependsGraph,dependsGraph.nodes());
          var deploy_promise = null;
          var contract_name ; //This is in global scope so that it can be used in the .catch below

         //Iterate over the dependency grpah in post order, deploy libraries first so we can
          //capture their addresses and use them to deploy the contracts that depend on them
          for(var i =0 ; i< dependsOrdering.length ;i++)
          {
              contract_name = dependsOrdering[i];
              var contract_class = config.contracts.classes[contract_name];

              if (contract_class == null) {
                c(new DeployError(`Could not find contract '${key}' for deployment. Check truffle.json.`));
                return;
              }

              var contract_data = { from: coinbase,
                                    gas: 3141592,
                                    //gasPrice: 50000000000, // 50 Shannon
                                    gasPrice: 100000000000, // 100 Shannon
                                    data: contract_class.binary
                                  };
              var dependencies = dependsGraph.successors(contract_name);

              //When we encounter a Library that is not dependant on other libraries, we can just
              //deploy it as normal
              if(dependencies.length == 0 )
              {
                 var contract = Pudding.whisk({
                  abi: contract_class.abi,
                  binary: contract_class.binary,
                  contract_name: contract_name
                });

                deploy_promise = this.createContractAndWait(config,contract_data,contract_name);

                //Store the promise in the graph so we can fetch it later
                dependsGraph.setNode(contract_name,deploy_promise);
              }
              //Contracts that have dependencies need to wait until those dependencies have been deployed
              //so we can inject the address into their byte code
              else
              {
                //Collect all the promises for the libraries this contract depends on into a list
                //NOTE: since this loop is traversing in post-order, we can be assured that this list
                //will contain ALL of the dependencies of this contract
                var depends_promises = dependencies.map(dependsGraph.node,dependsGraph);

                //Wait for all the dependencies to be committed and then do the linking step
                deploy_promise = Promise.all(depends_promises)
                                        .then(this.link_dependencies(config,contract_data,contract_class,contract_name));

                //It's possible that this contract is a dependency of some other contract so we store
                //it in the graph just in case
                dependsGraph.setNode(contract_name,deploy_promise);

              }
          }
          ///Now wait for all of the outstanding deployments to complete
          Promise.all(dependsGraph.nodes().map(dependsGraph.node,dependsGraph))
          .then(function(deployed_contracts) {

            deployed_contracts.forEach(function(a){config.contracts.classes[a.name].address = a.address;});

            c();
          }).catch(function(err) {
            c(new DeployError(err.message, contract_name));
          });

      },
    (c) => {
        this.write_contracts(config, "built contract files", c);
    },
    (c) => {
        this.after_deploy(config, c);
      }
    ], (err) => {
      if (err != null) {
        done_deploying(err);
        return;
      } else {
        done_deploying();
      }
    });
  },

  after_deploy(config, done) {
    async.eachSeries(config.app.resolved.after_deploy, function(file, iterator_callback) {
      if (config.argv.quietDeploy == null) {
        console.log(`Running post deploy script ${file}...`);
      }
      Exec.file(config, file, iterator_callback);
    }, done);
  }
}

module.exports = Contracts;
<|endoftext|>


<commit_before>const TwigRenderer = require('@basalt/twig-renderer');
const sleep = require('sleep-promise');
const { getTwigNamespaceConfig } = require('@bolt/build-utils/manifest');
const { getConfig } = require('@bolt/build-utils/config-store');
const path = require('path');

let twigNamespaces;
let twigRenderer;

const STATES = {
  NOT_STARTED: 'NOT_STARTED',
  STARTING: 'STARTING',
  READY: 'READY',
};
let state = STATES.NOT_STARTED;

/**
 * Initialize Twig Renderer instance
 * @param {Boolean} keepAlive - Optionally tell the Twig renderer service to keep alive to help speed up requests
 */
async function init(keepAlive = false) {
  state = STATES.STARTING;
  const config = await getConfig();
  const relativeFrom = path.dirname(config.configFileUsed);
  // console.log({ config });
  twigNamespaces = await getTwigNamespaceConfig(
    relativeFrom,
    config.extraTwigNamespaces,
  );
  twigRenderer = new TwigRenderer({
    relativeFrom,
    src: {
      roots: [relativeFrom],
      namespaces: TwigRenderer.convertLegacyNamespacesConfig(twigNamespaces),
    },
    debug: true,
    alterTwigEnv: config.alterTwigEnv,
    hasExtraInfoInResponses: false, // Will add `info` onto results with a lot of info about Twig Env
    maxConcurrency: 50,
    keepAlive, // only set this to be true when doing in-browser requests to avoid issues with this process not exiting when complete
  });
  state = STATES.READY;
}

/**
 * Stops any currently running Twig Renderer instances
 */
async function stop() {
  twigRenderer.stop();
}

async function prep(keepAlive) {
  switch (state) {
    case STATES.READY:
      return;
    case STATES.NOT_STARTED:
      return await init(keepAlive);
    case STATES.STARTING:
      while (state === STATES.STARTING) {
        await sleep(20); // eslint-disable-line no-await-in-loop
      }
  }
}

/**
 * Render Twig Template
 * @param {string} template - Template name (i.e. `@bolt/button.twig`)
 * @param {Object} data - Optional data to pass to template
 * @param {Boolean} keepAlive - Optionally tell the Twig renderer service to keep alive to help speed up requests
 * @return {Promise<{ ok: boolean, html: string, message: string }>} - Results of render
 */
async function render(template, data = {}, keepAlive = false) {
  await prep(keepAlive);
  const results = await twigRenderer.render(template, data);
  return results;
}

/**
 * Render Twig String
 * @param {string} templateString - String that is a Twig template (i.e. `<p>{{ text }}</p>`)
 * @param {Object} data - Optional data to pass to template
 * @param {Boolean} keepAlive - Optionally tell the Twig renderer service to keep alive to help speed up requests
 * @return {Promise<{ ok: boolean, html: string, message: string }>} - Results of render
 */
async function renderString(templateString, data = {}, keepAlive = false) {
  await prep(keepAlive);
  const results = await twigRenderer.renderString(templateString, data);
  // console.log({ results });
  return results;
}

// Both `render` and `renderString` will return something like this on success:
//   {
//     ok: true,
//     html: '
Hello World
',
//   }
// And something like this on failure:
//   {
//     ok: false,
//     message: 'The confabulator was not configured correctly',
//   }

module.exports = {
  render,
  renderString,
  stop,
};
<commit_msg>test: reduce max concurrency to 10<commit_after>const TwigRenderer = require('@basalt/twig-renderer');
const sleep = require('sleep-promise');
const { getTwigNamespaceConfig } = require('@bolt/build-utils/manifest');
const { getConfig } = require('@bolt/build-utils/config-store');
const path = require('path');

let twigNamespaces;
let twigRenderer;

const STATES = {
  NOT_STARTED: 'NOT_STARTED',
  STARTING: 'STARTING',
  READY: 'READY',
};
let state = STATES.NOT_STARTED;

/**
 * Initialize Twig Renderer instance
 * @param {Boolean} keepAlive - Optionally tell the Twig renderer service to keep alive to help speed up requests
 */
async function init(keepAlive = false) {
  state = STATES.STARTING;
  const config = await getConfig();
  const relativeFrom = path.dirname(config.configFileUsed);
  // console.log({ config });
  twigNamespaces = await getTwigNamespaceConfig(
    relativeFrom,
    config.extraTwigNamespaces,
  );
  twigRenderer = new TwigRenderer({
    relativeFrom,
    src: {
      roots: [relativeFrom],
      namespaces: TwigRenderer.convertLegacyNamespacesConfig(twigNamespaces),
    },
    debug: true,
    alterTwigEnv: config.alterTwigEnv,
    hasExtraInfoInResponses: false, // Will add `info` onto results with a lot of info about Twig Env
    maxConcurrency: 10,
    keepAlive, // only set this to be true when doing in-browser requests to avoid issues with this process not exiting when complete
  });
  state = STATES.READY;
}

/**
 * Stops any currently running Twig Renderer instances
 */
async function stop() {
  twigRenderer.stop();
}

async function prep(keepAlive) {
  switch (state) {
    case STATES.READY:
      return;
    case STATES.NOT_STARTED:
      return await init(keepAlive);
    case STATES.STARTING:
      while (state === STATES.STARTING) {
        await sleep(20); // eslint-disable-line no-await-in-loop
      }
  }
}

/**
 * Render Twig Template
 * @param {string} template - Template name (i.e. `@bolt/button.twig`)
 * @param {Object} data - Optional data to pass to template
 * @param {Boolean} keepAlive - Optionally tell the Twig renderer service to keep alive to help speed up requests
 * @return {Promise<{ ok: boolean, html: string, message: string }>} - Results of render
 */
async function render(template, data = {}, keepAlive = false) {
  await prep(keepAlive);
  const results = await twigRenderer.render(template, data);
  return results;
}

/**
 * Render Twig String
 * @param {string} templateString - String that is a Twig template (i.e. `<p>{{ text }}</p>`)
 * @param {Object} data - Optional data to pass to template
 * @param {Boolean} keepAlive - Optionally tell the Twig renderer service to keep alive to help speed up requests
 * @return {Promise<{ ok: boolean, html: string, message: string }>} - Results of render
 */
async function renderString(templateString, data = {}, keepAlive = false) {
  await prep(keepAlive);
  const results = await twigRenderer.renderString(templateString, data);
  // console.log({ results });
  return results;
}

// Both `render` and `renderString` will return something like this on success:
//   {
//     ok: true,
//     html: '
Hello World
',
//   }
// And something like this on failure:
//   {
//     ok: false,
//     message: 'The confabulator was not configured correctly',
//   }

module.exports = {
  render,
  renderString,
  stop,
};
<|endoftext|>