<commit_before>/**
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var events = require('events'),
    nodeutil = require('util');

var conn = require('../common/connection.js'),
    util = require('../common/util.js');

/**
 * Base URL for Pub/Sub API.
 * @type {String}
 */
var PUBSUB_BASE_URL = 'https://www.googleapis.com/pubsub/v1beta1';
/**
 * Required scopes for Pub/Sub API.
 * @type {Array}
 */
var SCOPES = [
  'https://www.googleapis.com/auth/pubsub',
  'https://www.googleapis.com/auth/cloud-platform'
];

function Subscription(conn, name) {
  var that = this;
  this.conn = conn;
  this.name = name;

  this.autoAck = false;
  this.pullIntervalInMs = 10;
  this.closed = false;
}

nodeutil.inherits(Subscription, events.EventEmitter);

/**
 * Acknowledges the backend that message is retrieved.
 * @param  {Array<String>}  ids      A list of message IDs.
 * @param  {Function}       callback Callback function.
 */
Subscription.prototype.ack = function(ids, callback) {
  ids = util.arrayize(ids);
  var body = {
    subscription: this.name,
    ackId: ids
  };
  this.conn.makeReq('POST', 'subscriptions/acknowledge', null, body, callback);
};

/**
 * Pulls from the subscribed topic.
 * @param  {Boolean}   opts.returnImmediately  If set, the system will respond immediately.
 *                                             Otherwise, wait until new messages are
 *                                             available. Returns if timeout is reached.
 * @param  {Function}  callback                Callback.
 */
Subscription.prototype.pull = function(opts, callback) {
  var that = this;
  // TODO(jbd): Should not be racing with other pull.
  // TOOD(jbd): Make opts optional.
  var body = {
    subscription: this.name,
    returnImmediately: !!opts.returnImmediately
  };
  this.conn.makeReq('POST', 'subscriptions/pull', null, body, function(err, message) {
    // TODO(jbd): Fix API to return a list of messages.
    if (err) {
      callback(err);
      return;
    }
    if (!that.autoAck) {
      that.emitMessage_(message);
      callback();
      return;
    }
    that.ack(message.ackId, function(err) {
      if (err) {
        callback(err);
        return;
      }
      that.emitMessage_(message);
      callback();
    });
  });
};

/**
 * Polls the backend for new messages.
 */
Subscription.prototype.startPulling_ = function() {
  var that = this;
  var pullFn = function() {
    if (that.closed) {
      return;
    }
    that.pull({ returnImmediately: false }, function(err) {
      // TODO(jbd): Fix API to return a more explicit error code or message.
      if (err && err.message.indexOf('has no more messages') < 0) {
        that.emitError_(err);
      }
      setTimeout(function() {
        pullFn();
      }, that.pullIntervalInMs);
    });
  };
  pullFn();
}

/**
 * Deletes the current subscription. Pull requests from the current
 * subscription will be errored once unsubscription is done.
 * @param  {Function} opt_callback Optional callback.
 */
Subscription.prototype.del = function(opt_callback) {
  var that = this;
  cb = opt_callback || util.noop;
  var path = util.format('subscriptions/{fullName}', {
    fullName: this.name
  });
  this.conn.makeReq('DELETE', path, null, true, function(err) {
    if (err) return cb(err);
    that.closed = true;
    cb(err);
  });
};

/**
 * Closes the subscription.
 */
Subscription.prototype.close = function() {
  this.closed = true;
};

/**
 * Emits a 'message' event with the provided message.
 */
Subscription.prototype.emitMessage_ = function(msg) {
  if (msg.pubsubEvent && msg.pubsubEvent.message) {
    var data = msg.pubsubEvent.message.data;
    msg.pubsubEvent.message.data = new Buffer(data, 'base64').toString('utf-8');
  }
  this.emit('message', msg);
};

/**
 * Emits an error with the provided error.
 */
Subscription.prototype.emitError_ = function(err) {
  this.emit('error', err);
};

/**
 * Represents a Google Cloud Pub/Sub API topic.
 * @param {Connection} conn  Authorized connection.
 * @param {string}     name  Full name of the topic.
 */
function Topic(conn, name) {
  this.conn = conn;
  this.name = name;
}

/**
 * Publishes the provided string message.
 * @param  {string}   data         String message to publish.
 * @param  {Function} opt_callback Optional callback.
 */
Topic.prototype.publish = function(data, opt_callback) {
  this.publishMessage({
    topic: this.name,
    message: {
      data: new Buffer(data).toString('base64')
    }
  }, opt_callback);
};

/**
 * Publishes a raw message.
 * @param  {message}  message       Raw message to publish.
 * @param  {Function} opt_callback  Optional callback.
 */
Topic.prototype.publishMessage = function(message, opt_callback) {
  cb = opt_callback || util.noop;
  message.topic = this.name;
  this.conn.makeReq('POST', 'topics/publish', null, message, cb);
};

/**
 * Deletes a topic.
 * @param  {Function} opt_callback Optional callback.
 */
Topic.prototype.del = function(opt_callback) {
  var path = 'topics/' + this.name;
  this.conn.makeReq('DELETE', path, null, true, cb);
};

/**
 * Represents connection to Google Cloud Pub/Sub API.
 * @param {string} opts.projectId   Google Developers Console Project ID.
 * @param {string} opts.email       Service account email.
 * @param {string} opts.pemFilePath Path to the pem file that contains your
 *                                  private key.
 */
function Connection(opts) {
  opts = opts || {};
  var id = opts.projectId;

  this.id = id;
  this.conn = new conn.Connection({
    email: opts.email,
    privateKey: opts.pemFilePath,
    scopes: SCOPES
  });
}

/**
 * Lists subscriptions.
 * @param  {string}   query.filterByTopic Returns subscriptions that are
 *                                        subscribed to the topic provided.
 * @param  {string}   query.pageToken     Page token.
 * @param  {Number}   query.maxResults    Max number of results to return.
 * @param  {Function} callback            Callback function.
 */
Connection.prototype.listSubscriptions = function(query, callback) {
  var that = this;
  if (arguments.length < 2) {
    callback = query;
    query = {};
  }
  var q = util.extend({}, query);
  q.query = q.filterByTopic
      ? 'pubsub.googleapis.com/topic in (' + this.fullTopicName_(q.filterByTopic) + ')'
      : 'cloud.googleapis.com/project in (' + this.fullProjectName_() + ')';
  delete q.filterByTopic;

  this.makeReq('GET', 'subscriptions', q, true, function(err, result) {
    if (err) {
      return callback(err);
    }
    var items = result.subscription || [];
    var subscriptions = items.map(function(item) {
      return new Subscription(that, item.name);
    });
    var nextQuery = null;
    if (result.nextPageToken) {
      nextQuery = q;
      nextQuery.pageToken = result.nextPageToken;
    }
    callback(null, subscriptions, nextQuery);
  });
};

// TODO(jbd): Create subscription

/**
 * Subscribe with the provided options.
 * @param  {string}   name           Name of the subscription.
 * @param  {Boolean}  opts.autoAck   Automatically acknowledges the
 *                                   message once it's pulled.
 * @return {Subscription}
 */
Connection.prototype.subscribe = function(name, opts) {
  var that = this;
  var fullName = '/subscriptions/' + this.id + '/' + name;
  var sub = new Subscription(that, fullName);
  sub.autoAck = !!opts.autoAck;
  this.makeReq('GET', 'subscriptions/' + fullName, null, true, function(err) {
    if (err) {
      sub.emitError_(err);
      return;
    }
    sub.emit('ready');
    sub.startPulling_();
  });
  return sub;
};

/**
 * Lists topics.
 * @param  {string}   query.pageToken     Page token.
 * @param  {Number}   query.maxResults    Max number of results to return.
 * @param  {Function} callback            Callback function.
 */
Connection.prototype.listTopics = function(query, callback) {
  var that = this;
  if (arguments.length < 2) {
    callback = query;
    query = {};
  }
  var q = util.extend({}, query);
  q.query = 'cloud.googleapis.com/project in (' + this.fullProjectName_() + ')';
  this.makeReq('GET', 'topics', q, true, function(err, result) {
    if (err) { return callback(err); }
    var items = result.topic || [];
    var topics = items.map(function(item) {
      return new Topic(that, item.name);
    });
    var nextQuery = null;
    if (result.nextPageToken) {
      nextQuery = q;
      nextQuery.pageToken = result.nextPageToken;
    }
    callback(null, topics, nextQuery);
  });
};

/**
 * Gets a topic.
 * @param  {string}   name         Name of the topic to get.
 * @param  {Function} opt_callback Optional callback.
 */
Connection.prototype.getTopic = function(name, opt_callback) {
  var that = this;
  var cb = opt_callback || util.noop,
      fullName = this.fullTopicName_(name);
  this.makeReq('GET', 'topics/' + fullName, null, true, function(err) {
    if (err) {
      return cb(err);
    }
    cb(null, new Topic(that, fullName));
  });
};

/**
 * Creates a topic with the given name.
 * @param  {string}   name         Name of the topic.
 * @param  {Function} opt_callback Optional callback.
 */
Connection.prototype.createTopic = function(name, opt_callback) {
  var that = this;
  var cb = opt_callback || util.noop,
      fullName = this.fullTopicName_(name);
  this.makeReq('POST', 'topics', null, { name: fullName }, function(err) {
    cb(err, new Topic(that, fullName));
  });
};

/**
 * Returns the full name of a topic.
 * Full name is in /topics/<projectId>/<shortName> form.
 */
Connection.prototype.fullTopicName_ = function(name) {
  return util.format('/topics/{projectId}/{name}', {
    projectId: this.id, name: name
  });
};

/**
 * Returns the fully qualified project name.
 * Full name is in /projects/<projectId> form.
 */
Connection.prototype.fullProjectName_ = function() {
  return util.format('/projects/{projectId}', {
    projectId: this.id
  });
};

// TOOD(jbd): Don't duplicate, unify this with bucket.makeReq.
Connection.prototype.makeReq = function(method, path, q, body, callback) {
  var reqOpts = {
    method: method,
    qs: q,
    uri: util.format('{base}/{path}', {
      base: PUBSUB_BASE_URL,
      path: path
    })
  };
  if (body) {
    reqOpts.json = body;
  }
  this.conn.req(reqOpts, function(err, res, body) {
    if (body && body.error) {
      return callback(body.error);
    }
    if (res && (res.statusCode < 200 || res.statusCode > 299)) {
      return callback(new Error('error during request, statusCode: ' + res.statusCode));
    }
    callback(err, body);
  });
};

/**
 * Exports Connection.
 */
module.exports.Connection = Connection;
<commit_msg>Allow options to be optional.<commit_after>/**
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var events = require('events'),
    nodeutil = require('util');

var conn = require('../common/connection.js'),
    util = require('../common/util.js');

/**
 * Base URL for Pub/Sub API.
 * @type {String}
 */
var PUBSUB_BASE_URL = 'https://www.googleapis.com/pubsub/v1beta1';
/**
 * Required scopes for Pub/Sub API.
 * @type {Array}
 */
var SCOPES = [
  'https://www.googleapis.com/auth/pubsub',
  'https://www.googleapis.com/auth/cloud-platform'
];

function Subscription(conn, name) {
  var that = this;
  this.conn = conn;
  this.name = name;

  this.autoAck = false;
  this.pullIntervalInMs = 10;
  this.closed = false;
}

nodeutil.inherits(Subscription, events.EventEmitter);

/**
 * Acknowledges the backend that message is retrieved.
 * @param  {Array<String>}  ids      A list of message IDs.
 * @param  {Function}       callback Callback function.
 */
Subscription.prototype.ack = function(ids, callback) {
  ids = util.arrayize(ids);
  var body = {
    subscription: this.name,
    ackId: ids
  };
  this.conn.makeReq('POST', 'subscriptions/acknowledge', null, body, callback);
};

/**
 * Pulls from the subscribed topic.
 * @param  {Boolean}   opts.returnImmediately  If set, the system will respond immediately.
 *                                             Otherwise, wait until new messages are
 *                                             available. Returns if timeout is reached.
 * @param  {Function}  callback                Callback.
 */
Subscription.prototype.pull = function(opts, callback) {
  var that = this;
  // TODO(jbd): Should not be racing with other pull.
  // TOOD(jbd): Make opts optional.
  var body = {
    subscription: this.name,
    returnImmediately: !!opts.returnImmediately
  };
  this.conn.makeReq('POST', 'subscriptions/pull', null, body, function(err, message) {
    // TODO(jbd): Fix API to return a list of messages.
    if (err) {
      callback(err);
      return;
    }
    if (!that.autoAck) {
      that.emitMessage_(message);
      callback();
      return;
    }
    that.ack(message.ackId, function(err) {
      if (err) {
        callback(err);
        return;
      }
      that.emitMessage_(message);
      callback();
    });
  });
};

/**
 * Polls the backend for new messages.
 */
Subscription.prototype.startPulling_ = function() {
  var that = this;
  var pullFn = function() {
    if (that.closed) {
      return;
    }
    that.pull({ returnImmediately: false }, function(err) {
      // TODO(jbd): Fix API to return a more explicit error code or message.
      if (err && err.message.indexOf('has no more messages') < 0) {
        that.emitError_(err);
      }
      setTimeout(function() {
        pullFn();
      }, that.pullIntervalInMs);
    });
  };
  pullFn();
}

/**
 * Deletes the current subscription. Pull requests from the current
 * subscription will be errored once unsubscription is done.
 * @param  {Function} opt_callback Optional callback.
 */
Subscription.prototype.del = function(opt_callback) {
  var that = this;
  cb = opt_callback || util.noop;
  var path = util.format('subscriptions/{fullName}', {
    fullName: this.name
  });
  this.conn.makeReq('DELETE', path, null, true, function(err) {
    if (err) return cb(err);
    that.closed = true;
    cb(err);
  });
};

/**
 * Closes the subscription.
 */
Subscription.prototype.close = function() {
  this.closed = true;
};

/**
 * Emits a 'message' event with the provided message.
 */
Subscription.prototype.emitMessage_ = function(msg) {
  if (msg.pubsubEvent && msg.pubsubEvent.message) {
    var data = msg.pubsubEvent.message.data;
    msg.pubsubEvent.message.data = new Buffer(data, 'base64').toString('utf-8');
  }
  this.emit('message', msg);
};

/**
 * Emits an error with the provided error.
 */
Subscription.prototype.emitError_ = function(err) {
  this.emit('error', err);
};

/**
 * Represents a Google Cloud Pub/Sub API topic.
 * @param {Connection} conn  Authorized connection.
 * @param {string}     name  Full name of the topic.
 */
function Topic(conn, name) {
  this.conn = conn;
  this.name = name;
}

/**
 * Publishes the provided string message.
 * @param  {string}   data         String message to publish.
 * @param  {Function} opt_callback Optional callback.
 */
Topic.prototype.publish = function(data, opt_callback) {
  this.publishMessage({
    topic: this.name,
    message: {
      data: new Buffer(data).toString('base64')
    }
  }, opt_callback);
};

/**
 * Publishes a raw message.
 * @param  {message}  message       Raw message to publish.
 * @param  {Function} opt_callback  Optional callback.
 */
Topic.prototype.publishMessage = function(message, opt_callback) {
  cb = opt_callback || util.noop;
  message.topic = this.name;
  this.conn.makeReq('POST', 'topics/publish', null, message, cb);
};

/**
 * Deletes a topic.
 * @param  {Function} opt_callback Optional callback.
 */
Topic.prototype.del = function(opt_callback) {
  var path = 'topics/' + this.name;
  this.conn.makeReq('DELETE', path, null, true, cb);
};

/**
 * Represents connection to Google Cloud Pub/Sub API.
 * @param {string} opts.projectId   Google Developers Console Project ID.
 * @param {string} opts.email       Service account email.
 * @param {string} opts.pemFilePath Path to the pem file that contains your
 *                                  private key.
 */
function Connection(opts) {
  opts = opts || {};
  var id = opts.projectId;

  this.id = id;
  this.conn = new conn.Connection({
    email: opts.email,
    privateKey: opts.pemFilePath,
    scopes: SCOPES
  });
}

/**
 * Lists subscriptions.
 * @param  {string}   query.filterByTopic Returns subscriptions that are
 *                                        subscribed to the topic provided.
 * @param  {string}   query.pageToken     Page token.
 * @param  {Number}   query.maxResults    Max number of results to return.
 * @param  {Function} callback            Callback function.
 */
Connection.prototype.listSubscriptions = function(query, callback) {
  var that = this;
  if (arguments.length < 2) {
    callback = query;
    query = {};
  }
  var q = util.extend({}, query);
  q.query = q.filterByTopic
      ? 'pubsub.googleapis.com/topic in (' + this.fullTopicName_(q.filterByTopic) + ')'
      : 'cloud.googleapis.com/project in (' + this.fullProjectName_() + ')';
  delete q.filterByTopic;

  this.makeReq('GET', 'subscriptions', q, true, function(err, result) {
    if (err) {
      return callback(err);
    }
    var items = result.subscription || [];
    var subscriptions = items.map(function(item) {
      return new Subscription(that, item.name);
    });
    var nextQuery = null;
    if (result.nextPageToken) {
      nextQuery = q;
      nextQuery.pageToken = result.nextPageToken;
    }
    callback(null, subscriptions, nextQuery);
  });
};

// TODO(jbd): Create subscription

/**
 * Subscribe with the provided options.
 * @param  {string}   name           Name of the subscription.
 * @param  {Boolean}  opts.autoAck   Automatically acknowledges the
 *                                   message once it's pulled.
 * @return {Subscription}
 */
Connection.prototype.subscribe = function(name, opts) {
  var that = this;
  opts = opts || {};
  var fullName = '/subscriptions/' + this.id + '/' + name;
  var sub = new Subscription(that, fullName);
  sub.autoAck = !!opts.autoAck;
  this.makeReq('GET', 'subscriptions/' + fullName, null, true, function(err) {
    if (err) {
      sub.emitError_(err);
      return;
    }
    sub.emit('ready');
    sub.startPulling_();
  });
  return sub;
};

/**
 * Lists topics.
 * @param  {string}   query.pageToken     Page token.
 * @param  {Number}   query.maxResults    Max number of results to return.
 * @param  {Function} callback            Callback function.
 */
Connection.prototype.listTopics = function(query, callback) {
  var that = this;
  if (arguments.length < 2) {
    callback = query;
    query = {};
  }
  var q = util.extend({}, query);
  q.query = 'cloud.googleapis.com/project in (' + this.fullProjectName_() + ')';
  this.makeReq('GET', 'topics', q, true, function(err, result) {
    if (err) { return callback(err); }
    var items = result.topic || [];
    var topics = items.map(function(item) {
      return new Topic(that, item.name);
    });
    var nextQuery = null;
    if (result.nextPageToken) {
      nextQuery = q;
      nextQuery.pageToken = result.nextPageToken;
    }
    callback(null, topics, nextQuery);
  });
};

/**
 * Gets a topic.
 * @param  {string}   name         Name of the topic to get.
 * @param  {Function} opt_callback Optional callback.
 */
Connection.prototype.getTopic = function(name, opt_callback) {
  var that = this;
  var cb = opt_callback || util.noop,
      fullName = this.fullTopicName_(name);
  this.makeReq('GET', 'topics/' + fullName, null, true, function(err) {
    if (err) {
      return cb(err);
    }
    cb(null, new Topic(that, fullName));
  });
};

/**
 * Creates a topic with the given name.
 * @param  {string}   name         Name of the topic.
 * @param  {Function} opt_callback Optional callback.
 */
Connection.prototype.createTopic = function(name, opt_callback) {
  var that = this;
  var cb = opt_callback || util.noop,
      fullName = this.fullTopicName_(name);
  this.makeReq('POST', 'topics', null, { name: fullName }, function(err) {
    cb(err, new Topic(that, fullName));
  });
};

/**
 * Returns the full name of a topic.
 * Full name is in /topics/<projectId>/<shortName> form.
 */
Connection.prototype.fullTopicName_ = function(name) {
  return util.format('/topics/{projectId}/{name}', {
    projectId: this.id, name: name
  });
};

/**
 * Returns the fully qualified project name.
 * Full name is in /projects/<projectId> form.
 */
Connection.prototype.fullProjectName_ = function() {
  return util.format('/projects/{projectId}', {
    projectId: this.id
  });
};

// TOOD(jbd): Don't duplicate, unify this with bucket.makeReq.
Connection.prototype.makeReq = function(method, path, q, body, callback) {
  var reqOpts = {
    method: method,
    qs: q,
    uri: util.format('{base}/{path}', {
      base: PUBSUB_BASE_URL,
      path: path
    })
  };
  if (body) {
    reqOpts.json = body;
  }
  this.conn.req(reqOpts, function(err, res, body) {
    if (body && body.error) {
      return callback(body.error);
    }
    if (res && (res.statusCode < 200 || res.statusCode > 299)) {
      return callback(new Error('error during request, statusCode: ' + res.statusCode));
    }
    callback(err, body);
  });
};

/**
 * Exports Connection.
 */
module.exports.Connection = Connection;
<|endoftext|>


<commit_before>var Handlebars = require('handlebars')
var Backbone = require('backbone')
var moment = require('moment')
var $ = require("jquery")

require("eonasdan-bootstrap-datetimepicker")

var BikeTriangleControl = require('./bike-triangle-control')
var geocoder = require('./geocoder')
var log = require('./log')('request-form')
var rfCommon = require('./request-form-common')

var requestFormTemplate = Handlebars.compile(require('./templates/request-form.html'))

var defaultModes = [ 'TRANSIT,WALK', 'SUBWAY,TRAM,RAIL,WALK', 'BUS,WALK', 'WALK', 'BICYCLE', 'TRANSIT,BICYCLE' ]

var RequestView = Backbone.View.extend({
  events: {
    'change .apiParam': 'changeForm',
    'click .reverse-direction': 'reverseDirection',
    'click .search-button': 'changeForm',
    'change #mode': 'updateModeControls',
    'click .toggleSettings': 'toggleSettings',
    'click .first': 'first',
    'click .previous': 'previous',
    'click .next': 'next',
    'click .last': 'last'
  },

  first: function () {
    this.model.set({
      arriveBy: false,
      time: '12:01 am'
    })
  },

  previous: function () {
    var active = this.itineraries.activeItinerary
    var endTime = moment(active.get('endTime'))
    var date = endTime.add(this.timeOffset, 'hours').subtract(1, 'minute')

    this.model.set({
      arriveBy: true,
      date: date.format('MM/DD/YYYY'),
      time: date.format('hh:mm A')
    })
  },

  next: function () {
    var active = this.itineraries.activeItinerary
    var startTime = moment(active.get('startTime'))
    var date = startTime.add(this.timeOffset, 'hours').add(1, 'minute')

    this.model.set({
      arriveBy: false,
      date: date.format('MM/DD/YYYY'),
      time: date.format('hh:mm A')
    })
  },

  last: function () {
    this.model.set({
      arriveBy: false,
      time: '11:59 pm'
    })
  },

  setNextPreviousLastHidden: function (hidden) {
    if (hidden) {
      this.$('.nextPreviousLast').addClass('hidden')
    } else {
      this.$('.nextPreviousLast').removeClass('hidden')
    }
  },

  initialize: function (options) {
    this.options = options || {}
    _.bindAll(this, 'changeForm')

    var view = this

    this.updatingForm = false

    this.geocodeItem = Handlebars.compile([
      '{{text}}',
      '{{#if city}}{{city}}, {{/if}}{{state}}'
    ].join('\n'))

    this.selectedGeocodeItem = Handlebars.compile([
      '{{text}}{{#if city}}, {{city}}{{/if}}{{#if state}}, {{state}}{{/if}}'
    ].join('\n'))

    this.listenTo(this.model, 'change', function (data) {
      log('updating form with model changes')

      if (_.has(data.changed, 'fromPlace') && data.attributes.fromPlace && view.selectFrom && !this.skipReverseGeocode) {
        view.updateReverseGeocoder('From', data.attributes.fromPlace, view.selectFrom)
      }
      if (_.has(data.changed, 'toPlace') && data.attributes.toPlace && view.selectTo && !this.skipReverseGeocode) {
        view.updateReverseGeocoder('To', data.attributes.toPlace, view.selectTo)
      }
      this.skipReverseGeocode = false

      if (data.attributes.arriveBy !== undefined) {
        view.$('#arriveBy').val(data.attributes.arriveBy + '')
      }

      if (data.attributes.mode) view.$('#mode').val(data.attributes.mode)
      if (data.attributes.maxWalkDistance) view.$('#maxWalkDistance').val(data.attributes.maxWalkDistance)
      if (data.attributes.optimize) view.$('#optimize').val(data.attributes.optimize)

      if (data.attributes.date) {
        var date = moment(data.attributes.date, 'MM-DD-YYYY').toDate()
        view.datepicker.date(date)
      }

      if (data.attributes.time) {
        var time = moment(data.attributes.time, 'hh:mm a').toDate()
        view.timepicker.date(time)
      }

      if (data.attributes.wheelchair === 'true') {
        view.$('#wheelchair').prop('checked', true)
      }

      view.updateModeControls()
    })

    this.listenTo(this.model, 'requesting', function () {
      view.deactivateSearchButton()
      view.setNextPreviousLastHidden(true)
    })

    this.listenTo(this.model, 'success', function (response) {
      view.activateSearchButton()
      view.setNextPreviousLastHidden(false)
      view.timeOffset = response.getTimeOffset()
      view.itineraries = response.get('itineraries')
    })

    this.listenTo(this.model, 'failure', function () {
      view.activateSearchButton()
      view.setNextPreviousLastHidden(true)
    })
  },

  updateReverseGeocoder: function (field, latlon, select) {
    log('updating %s with %s', field, latlon)
    var view = this

    if (window.OTP_config.reverseGeocode) {
      view.updatingForm = true
      geocoder.reverse(latlon, function (err, results) {
        if (err) {
          select.select2('data', [])
          select.select2('data', {
            text: field + ' marker location',
            place: latlon
          })
        } else {
          select.select2('data', null)
          results.text = field + ' ' + results.address
          select.select2('data', results)
        }
        view.updatingForm = false
      })
    } else {
      // this isn't great but appears to be neccesary as selectize triggers a change event even when programatically updated
      view.updatingForm = true

      var item = {
        text: field + ' marker location',
        place: latlon
      }

      select.select2('data', item)

      view.updatingForm = false
    }
  },

  render: function () {
    log('rendering request form view')
    var view = this
    view.updatingForm = true

    var html = requestFormTemplate({
      metric: this.options.metric || false,
      modes: window.OTP_config.modes || defaultModes
    })

    this.lastResults = []

    this.$el.html(html)

    this.$('#hideSettings').hide()
    this.$('#hidableSettings').hide()
    this.$('#date').datetimepicker({
      format: 'MM/DD/YYYY'
    })

    this.datepicker = this.$('#date').data('DateTimePicker')
    this.datepicker.date(new Date())
    this.$('#date').on('dp.change', function () {
      view.changeForm()
    })

    this.$('#time').datetimepicker({
      format: 'hh:mm A'
    })

    this.timepicker = this.$('#time').data('DateTimePicker')
    this.timepicker.date(new Date())
    this.$('#time').on('dp.change', function () {
      view.changeForm()
    })

    var data = {
      date: this.$('#date input').val(),
      time: this.$('#time input').val(),
      maxWalkDistance: 8046
    }

    this.model.set(data)

    this.bikeTriangle = new BikeTriangleControl({
      model: this.model,
      el: this.$('#bikeTriangle')
    })

    this.selectFrom = rfCommon.initializeSelectFor('from', this.$('#fromPlace'), this)
    this.selectTo = rfCommon.initializeSelectFor('to', this.$('#toPlace'), this)

    this.updateModeControls()

    view.updatingForm = false
  },

  changeForm: function (evt) {
    // skip duplicate change events caused by selectize form inputs
    if (this.updatingForm) return

    this.updatingForm = true

    var maxDistance = $('#mode').val().indexOf('WALK') !== -1
      ? $('#maxWalkDistance').val()
      : $('#maxBikeDistance').val()

    var data = {
      date: this.$('#date input').val(),
      time: this.$('#time input').val(),
      arriveBy: this.$('#arriveBy').val(),
      maxWalkDistance: maxDistance,
      optimize: this.$('#optimize').val(),
      mode: this.$('#mode').val(),
      wheelchair: this.$('#wheelchair').prop('checked')
    }

    // skip if either to/from fields are unset
    var fromData = this.$('#fromPlace').select2('data')
    data.fromPlace = _.has(fromData, 'place') ? fromData.place : false

    var toData = this.$('#toPlace').select2('data')
    data.toPlace = _.has(toData, 'place') ? toData.place : false

    var mode = $('#mode').val()
    if (mode.indexOf('BICYCLE') !== -1) {
      data.triangleSafetyFactor = $('#').val()
      data.triangleSlopeFactor = $('#').val()
      data.triangleTimeFactor = $('#').val()
    } else {
      this.model.unset('triangleSafetyFactor', {
        silent: true
      })
      this.model.unset('triangleSlopeFactor', {
        silent: true
      })
      this.model.unset('triangleTimeFactor', {
        silent: true
      })
    }

    this.model.set(data)
    this.updatingForm = false
  },

  deactivateSearchButton: function () {
    log('deactivating search button')

    this.$('.search-button').addClass('hidden')
    this.$('.searching-button').removeClass('hidden')
  },

  activateSearchButton: function () {
    log('activating search button')

    this.$('.search-button').removeClass('hidden')
    this.$('.searching-button').addClass('hidden')
  },

  updateModeControls: function () {
    var mode = this.$('#mode').val()
    if (!mode) {
      this.$('#mode').val('TRANSIT,WALK')
      mode = 'TRANSIT,WALK'
    }

    // disabling maxWalkControl as we switch to soft walk dist limiting
    this.$el.find('.maxWalkControl').hide()

    if (mode.indexOf('BICYCLE') !== -1 && mode.indexOf('TRANSIT') !== -1) {
      this.$el.find('.maxBikeControl').show()
    } else {
      this.$el.find('.maxBikeControl').hide()
    }

    if (mode.indexOf('TRANSIT') !== -1 && mode.indexOf('BICYCLE') === -1) {
      this.$el.find('.optimizeControl').show()
    } else {
      this.$el.find('.optimizeControl').hide()
    }

    if (mode.indexOf('BICYCLE') !== -1) {
      this.$el.find('.bikeTriangleControl').show()
    } else {
      this.$el.find('.bikeTriangleControl').hide()
    }
  },

  toggleSettings: function () {
    if ($('#hidableSettings').is(':visible')) {
      $('#hidableSettings').slideUp('fast', function () {
        $('#itineraries').height($(window).height() - ($('#request').height() +
          $('#messageWell').height() + 80))
      })
      $('#showSettings').show()
      $('#hideSettings').hide()
      this.bikeTriangle.disable()
    } else {
      $('#hidableSettings').slideDown('fast', function () {
        $('#itineraries').height($(window).height() - ($('#request').height() +
          $('#messageWell').height() + 80))
      })
      $('#showSettings').hide()
      $('#hideSettings').show()
      if (!this.bikeTriangle.rendered) {
        this.bikeTriangle.render()
      }
      this.bikeTriangle.enable()
    }
  },

  reverse: function (place, error, success) {
    // esri takes lon/lat
    var parts = place.split(',')
    var lonlat = parts[1] + ',' + parts[0]

    $.ajax({
      url: window.OTP_config.esriApi + 'reverseGeocode?location=' +
        encodeURIComponent(lonlat) + '&f=pjson',
      type: 'GET',
      dataType: 'jsonp',
      error: function () {
        error()
      },
      success: function (res) {
        var data = {
          address: res.address.Address,
          city: res.address.City,
          state: res.address.Region,
          latlon: place
        }

        success(data)
      }
    })
  },

  reverseDirection: function (e) {
    e.preventDefault()
    this.model.set({
      toPlace: this.model.get('fromPlace'),
      fromPlace: this.model.get('toPlace')
    })
  }
})

// Expose RequestView

module.exports = RequestView
<commit_msg>fix datetime pickers config<commit_after>var Handlebars = require('handlebars')
var Backbone = require('backbone')
var moment = require('moment')
var $ = require("jquery")

require("eonasdan-bootstrap-datetimepicker")

var BikeTriangleControl = require('./bike-triangle-control')
var geocoder = require('./geocoder')
var log = require('./log')('request-form')
var rfCommon = require('./request-form-common')

var requestFormTemplate = Handlebars.compile(require('./templates/request-form.html'))

var defaultModes = [ 'TRANSIT,WALK', 'SUBWAY,TRAM,RAIL,WALK', 'BUS,WALK', 'WALK', 'BICYCLE', 'TRANSIT,BICYCLE' ]

var RequestView = Backbone.View.extend({
  events: {
    'change .apiParam': 'changeForm',
    'click .reverse-direction': 'reverseDirection',
    'click .search-button': 'changeForm',
    'change #mode': 'updateModeControls',
    'click .toggleSettings': 'toggleSettings',
    'click .first': 'first',
    'click .previous': 'previous',
    'click .next': 'next',
    'click .last': 'last'
  },

  first: function () {
    this.model.set({
      arriveBy: false,
      time: '12:01 am'
    })
  },

  previous: function () {
    var active = this.itineraries.activeItinerary
    var endTime = moment(active.get('endTime'))
    var date = endTime.add(this.timeOffset, 'hours').subtract(1, 'minute')

    this.model.set({
      arriveBy: true,
      date: date.format('MM/DD/YYYY'),
      time: date.format('hh:mm A')
    })
  },

  next: function () {
    var active = this.itineraries.activeItinerary
    var startTime = moment(active.get('startTime'))
    var date = startTime.add(this.timeOffset, 'hours').add(1, 'minute')

    this.model.set({
      arriveBy: false,
      date: date.format('MM/DD/YYYY'),
      time: date.format('hh:mm A')
    })
  },

  last: function () {
    this.model.set({
      arriveBy: false,
      time: '11:59 pm'
    })
  },

  setNextPreviousLastHidden: function (hidden) {
    if (hidden) {
      this.$('.nextPreviousLast').addClass('hidden')
    } else {
      this.$('.nextPreviousLast').removeClass('hidden')
    }
  },

  initialize: function (options) {
    this.options = options || {}
    _.bindAll(this, 'changeForm')

    var view = this

    this.updatingForm = false

    this.geocodeItem = Handlebars.compile([
      '{{text}}',
      '{{#if city}}{{city}}, {{/if}}{{state}}'
    ].join('\n'))

    this.selectedGeocodeItem = Handlebars.compile([
      '{{text}}{{#if city}}, {{city}}{{/if}}{{#if state}}, {{state}}{{/if}}'
    ].join('\n'))

    this.listenTo(this.model, 'change', function (data) {
      log('updating form with model changes')

      if (_.has(data.changed, 'fromPlace') && data.attributes.fromPlace && view.selectFrom && !this.skipReverseGeocode) {
        view.updateReverseGeocoder('From', data.attributes.fromPlace, view.selectFrom)
      }
      if (_.has(data.changed, 'toPlace') && data.attributes.toPlace && view.selectTo && !this.skipReverseGeocode) {
        view.updateReverseGeocoder('To', data.attributes.toPlace, view.selectTo)
      }
      this.skipReverseGeocode = false

      if (data.attributes.arriveBy !== undefined) {
        view.$('#arriveBy').val(data.attributes.arriveBy + '')
      }

      if (data.attributes.mode) view.$('#mode').val(data.attributes.mode)
      if (data.attributes.maxWalkDistance) view.$('#maxWalkDistance').val(data.attributes.maxWalkDistance)
      if (data.attributes.optimize) view.$('#optimize').val(data.attributes.optimize)

      if (data.attributes.date) {
        var date = moment(data.attributes.date, 'MM-DD-YYYY').toDate()
        view.datepicker.setDate(date)
      }

      if (data.attributes.time) {
        var time = moment(data.attributes.time, 'hh:mm a').toDate()
        view.timepicker.setDate(time)
      }

      if (data.attributes.wheelchair === 'true') {
        view.$('#wheelchair').prop('checked', true)
      }

      view.updateModeControls()
    })

    this.listenTo(this.model, 'requesting', function () {
      view.deactivateSearchButton()
      view.setNextPreviousLastHidden(true)
    })

    this.listenTo(this.model, 'success', function (response) {
      view.activateSearchButton()
      view.setNextPreviousLastHidden(false)
      view.timeOffset = response.getTimeOffset()
      view.itineraries = response.get('itineraries')
    })

    this.listenTo(this.model, 'failure', function () {
      view.activateSearchButton()
      view.setNextPreviousLastHidden(true)
    })
  },

  updateReverseGeocoder: function (field, latlon, select) {
    log('updating %s with %s', field, latlon)
    var view = this

    if (window.OTP_config.reverseGeocode) {
      view.updatingForm = true
      geocoder.reverse(latlon, function (err, results) {
        if (err) {
          select.select2('data', [])
          select.select2('data', {
            text: field + ' marker location',
            place: latlon
          })
        } else {
          select.select2('data', null)
          results.text = field + ' ' + results.address
          select.select2('data', results)
        }
        view.updatingForm = false
      })
    } else {
      // this isn't great but appears to be neccesary as selectize triggers a change event even when programatically updated
      view.updatingForm = true

      var item = {
        text: field + ' marker location',
        place: latlon
      }

      select.select2('data', item)

      view.updatingForm = false
    }
  },

  render: function () {
    log('rendering request form view')
    var view = this
    view.updatingForm = true

    var html = requestFormTemplate({
      metric: this.options.metric || false,
      modes: window.OTP_config.modes || defaultModes
    })

    this.lastResults = []

    this.$el.html(html)

    this.$('#hideSettings').hide()
    this.$('#hidableSettings').hide()
    this.$('#date').datetimepicker({
      pickTime: false
    })

    this.datepicker = this.$('#date').data('DateTimePicker')
    this.datepicker.setDate(new Date())
    this.$('#date').on('dp.change', function () {
      view.changeForm()
    })

    this.$('#time').datetimepicker({
      pick12HourFormat: true,
      pickSeconds: false,
      pickDate: false
    })

    this.timepicker = this.$('#time').data('DateTimePicker')
    this.timepicker.setDate(new Date())
    this.$('#time').on('dp.change', function () {
      view.changeForm()
    })

    var data = {
      date: this.$('#date input').val(),
      time: this.$('#time input').val(),
      maxWalkDistance: 8046
    }

    this.model.set(data)

    this.bikeTriangle = new BikeTriangleControl({
      model: this.model,
      el: this.$('#bikeTriangle')
    })

    this.selectFrom = rfCommon.initializeSelectFor('from', this.$('#fromPlace'), this)
    this.selectTo = rfCommon.initializeSelectFor('to', this.$('#toPlace'), this)

    this.updateModeControls()

    view.updatingForm = false
  },

  changeForm: function (evt) {
    // skip duplicate change events caused by selectize form inputs
    if (this.updatingForm) return

    this.updatingForm = true

    var maxDistance = $('#mode').val().indexOf('WALK') !== -1
      ? $('#maxWalkDistance').val()
      : $('#maxBikeDistance').val()

    var data = {
      date: this.$('#date input').val(),
      time: this.$('#time input').val(),
      arriveBy: this.$('#arriveBy').val(),
      maxWalkDistance: maxDistance,
      optimize: this.$('#optimize').val(),
      mode: this.$('#mode').val(),
      wheelchair: this.$('#wheelchair').prop('checked')
    }

    // skip if either to/from fields are unset
    var fromData = this.$('#fromPlace').select2('data')
    data.fromPlace = _.has(fromData, 'place') ? fromData.place : false

    var toData = this.$('#toPlace').select2('data')
    data.toPlace = _.has(toData, 'place') ? toData.place : false

    var mode = $('#mode').val()
    if (mode.indexOf('BICYCLE') !== -1) {
      data.triangleSafetyFactor = $('#').val()
      data.triangleSlopeFactor = $('#').val()
      data.triangleTimeFactor = $('#').val()
    } else {
      this.model.unset('triangleSafetyFactor', {
        silent: true
      })
      this.model.unset('triangleSlopeFactor', {
        silent: true
      })
      this.model.unset('triangleTimeFactor', {
        silent: true
      })
    }

    this.model.set(data)
    this.updatingForm = false
  },

  deactivateSearchButton: function () {
    log('deactivating search button')

    this.$('.search-button').addClass('hidden')
    this.$('.searching-button').removeClass('hidden')
  },

  activateSearchButton: function () {
    log('activating search button')

    this.$('.search-button').removeClass('hidden')
    this.$('.searching-button').addClass('hidden')
  },

  updateModeControls: function () {
    var mode = this.$('#mode').val()
    if (!mode) {
      this.$('#mode').val('TRANSIT,WALK')
      mode = 'TRANSIT,WALK'
    }

    // disabling maxWalkControl as we switch to soft walk dist limiting
    this.$el.find('.maxWalkControl').hide()

    if (mode.indexOf('BICYCLE') !== -1 && mode.indexOf('TRANSIT') !== -1) {
      this.$el.find('.maxBikeControl').show()
    } else {
      this.$el.find('.maxBikeControl').hide()
    }

    if (mode.indexOf('TRANSIT') !== -1 && mode.indexOf('BICYCLE') === -1) {
      this.$el.find('.optimizeControl').show()
    } else {
      this.$el.find('.optimizeControl').hide()
    }

    if (mode.indexOf('BICYCLE') !== -1) {
      this.$el.find('.bikeTriangleControl').show()
    } else {
      this.$el.find('.bikeTriangleControl').hide()
    }
  },

  toggleSettings: function () {
    if ($('#hidableSettings').is(':visible')) {
      $('#hidableSettings').slideUp('fast', function () {
        $('#itineraries').height($(window).height() - ($('#request').height() +
          $('#messageWell').height() + 80))
      })
      $('#showSettings').show()
      $('#hideSettings').hide()
      this.bikeTriangle.disable()
    } else {
      $('#hidableSettings').slideDown('fast', function () {
        $('#itineraries').height($(window).height() - ($('#request').height() +
          $('#messageWell').height() + 80))
      })
      $('#showSettings').hide()
      $('#hideSettings').show()
      if (!this.bikeTriangle.rendered) {
        this.bikeTriangle.render()
      }
      this.bikeTriangle.enable()
    }
  },

  reverse: function (place, error, success) {
    // esri takes lon/lat
    var parts = place.split(',')
    var lonlat = parts[1] + ',' + parts[0]

    $.ajax({
      url: window.OTP_config.esriApi + 'reverseGeocode?location=' +
        encodeURIComponent(lonlat) + '&f=pjson',
      type: 'GET',
      dataType: 'jsonp',
      error: function () {
        error()
      },
      success: function (res) {
        var data = {
          address: res.address.Address,
          city: res.address.City,
          state: res.address.Region,
          latlon: place
        }

        success(data)
      }
    })
  },

  reverseDirection: function (e) {
    e.preventDefault()
    this.model.set({
      toPlace: this.model.get('fromPlace'),
      fromPlace: this.model.get('toPlace')
    })
  }
})

// Expose RequestView

module.exports = RequestView
<|endoftext|>


<commit_before>import * as regionUtil from './regionUtil'
import humanizeList from 'humanize-list'

function listify(arr, options = {}) {
  const defaults = {oxfordComma: false, conjunction: 'and'}
  return humanizeList(arr, Object.assign({}, defaults, options))
}

const NO_NUMERIC = /\D/
function invalidYear(val) {
  return NO_NUMERIC.test(val)
}

export default function resolveQuery(region, srcQuery, headerGroups) {

  const targetQuery = Object.assign({}, {
    unit: srcQuery.unit,
    region: srcQuery.region,
    tableName: srcQuery.tableName,
    comparisonRegions: srcQuery.comparisonRegions || [],
    dimensions: srcQuery.dimensions.slice()
  })

  const regionHeaderKey = regionUtil.getHeaderKey(region)
  const headerGroupsForRegion = headerGroups.filter(group => group.hasOwnProperty(regionHeaderKey))

  const possibleValues = headerGroupsForRegion.find(headerGroup => {
    return srcQuery.dimensions.every(dim => headerGroup.hasOwnProperty(dim.name))
  })

  if (!possibleValues) {
    const combinationsForHeaderKey = headerGroupsForRegion
      .map(headerGroup => Object.keys(headerGroup).join(', '))
      .map(keys => `"${keys}"`)
    const dimNames = srcQuery.dimensions.map(dim => dim.name)
    throw new Error(
      `Found no header group with values for the combination of ${listify([regionHeaderKey, ...dimNames])}]. `
    + `Possible combinations for ${regionHeaderKey} are ${listify(combinationsForHeaderKey, {conjunction: 'or'})}`
    )
  }

  const {year = []} = srcQuery

  if (typeof year === 'string') {
    if (srcQuery.year === 'latest') {
      targetQuery.year = possibleValues.aar.slice(-1)
    } else if (srcQuery.year === 'all') {
      targetQuery.year = 'all'
    } else {
      throw new Error(`The .year property of a query must be either 'latest', 'all' or an array of years. Got ${JSON.stringify(year)}`)
    }
  } else if (Array.isArray(year)) {
    const invalidYears = year.filter(invalidYear)
    if (invalidYears.length > 0) {
      throw new Error(`Found invalid years in query.year: ${invalidYears.join(', ')}`)
    }
    targetQuery.year = year
  } else {
    throw new Error(`The .year property of a query must be either 'latest', 'all' or an array of years. Got ${JSON.stringify(year)}`)
  }
  return targetQuery
}
<commit_msg>Wrap combos in array<commit_after>import * as regionUtil from './regionUtil'
import humanizeList from 'humanize-list'

const NO_NUMERIC = /\D/
function invalidYear(val) {
  return NO_NUMERIC.test(val)
}

export default function resolveQuery(region, srcQuery, headerGroups) {

  const targetQuery = Object.assign({}, {
    unit: srcQuery.unit,
    region: srcQuery.region,
    tableName: srcQuery.tableName,
    comparisonRegions: srcQuery.comparisonRegions || [],
    dimensions: srcQuery.dimensions.slice()
  })

  const regionHeaderKey = regionUtil.getHeaderKey(region)
  const headerGroupsForRegion = headerGroups.filter(group => group.hasOwnProperty(regionHeaderKey))

  const possibleValues = headerGroupsForRegion.find(headerGroup => {
    return srcQuery.dimensions.every(dim => headerGroup.hasOwnProperty(dim.name))
  })

  if (!possibleValues) {
    const combinationsForHeaderKey = headerGroupsForRegion
      .map(headerGroup => Object.keys(headerGroup).join(', '))
      .map(keys => `[${keys}]`)
    const dimNames = srcQuery.dimensions.map(dim => dim.name)
    throw new Error(
      `Found no header group with values for the combination of ${humanizeList([regionHeaderKey, ...dimNames])}]. `
    + `Possible combinations for ${regionHeaderKey} are ${humanizeList(combinationsForHeaderKey, {conjunction: 'or'})}`
    )
  }

  const {year = []} = srcQuery

  if (typeof year === 'string') {
    if (srcQuery.year === 'latest') {
      targetQuery.year = possibleValues.aar.slice(-1)
    } else if (srcQuery.year === 'all') {
      targetQuery.year = 'all'
    } else {
      throw new Error(`The .year property of a query must be either 'latest', 'all' or an array of years. Got ${JSON.stringify(year)}`)
    }
  } else if (Array.isArray(year)) {
    const invalidYears = year.filter(invalidYear)
    if (invalidYears.length > 0) {
      throw new Error(`Found invalid years in query.year: ${invalidYears.join(', ')}`)
    }
    targetQuery.year = year
  } else {
    throw new Error(`The .year property of a query must be either 'latest', 'all' or an array of years. Got ${JSON.stringify(year)}`)
  }
  return targetQuery
}
<|endoftext|>


<commit_before>'use strict';

let koaRouterRequire = require('@koa/router');
let handle = require('./route-handler');
let log    = Bento.Log;
let koaRouter = new koaRouterRequire();
let verbList = ['post','get','put','delete'];

verbList.forEach(name => {
  koaRouter['_' + name] = koaRouter[name];
});

function process(verb, route, options) {
  return koaRouter['_' + verb](route, (ctx, next) => {
    function *wrap() {
      log.debug(`${ ctx.method } ${ ctx.path } | ${ ctx.auth.check() ? `${ ctx.auth.user.name() } <${ ctx.auth.user.email }> [ ${ ctx.auth.user.role.title } ]` : 'Guest' }`);

      options = handle.options(options);

      // Add the route to the ctx.route of the request context.
      ctx.route = route;

      // Prepares the incoming parameters and data payload before passing it onto the route handler.
      ctx.params  = yield handle.params(ctx.params);
      ctx.payload = yield handle.payload(ctx);

      // Make sure policies and required parameters has been verified.
      yield handle.checkPolicies(ctx, options.policy);
      yield handle.checkParameters(ctx, options.params);

      yield handle.route(ctx, options);
    }

    let instance = wrap();
    while(true) {
      let value = instance.next();
      if(value.done) {
        return next();
      }
    }
  });
};

/**
 * Runs middleware like operations on the provided route options before executing
 * the request controller.method
 */
let Route = global.Route = Object.assign(koaRouter, {
  resource: require('./resource'),

  post : (endpoint, options) => process('post', endpoint, options),
  get  : (endpoint, options) => process('get', endpoint, options),
  put  : (endpoint, options) => process('put', endpoint, options),
  del  : (endpoint, options) => process('delete', endpoint, options)
});

Route.pst = Route.post;
Route.delete = Route.del;
<commit_msg>attempting to fix koa again<commit_after>'use strict';

let koaRouterRequire = require('@koa/router');
let handle = require('./route-handler');
let log    = Bento.Log;
let koaRouter = new koaRouterRequire();
let verbList = ['post','get','put','delete'];
let co     = require('co');

verbList.forEach(name => {
  koaRouter['_' + name] = koaRouter[name];
});

function process(verb, route, options) {
  return koaRouter['_' + verb](route, async (ctx, next) => {
    await co(function *() {
      log.debug(`${ ctx.method } ${ ctx.path } | ${ ctx.auth.check() ? `${ ctx.auth.user.name() } <${ ctx.auth.user.email }> [ ${ ctx.auth.user.role.title } ]` : 'Guest' }`);

      options = handle.options(options);

      // Add the route to the ctx.route of the request context.
      ctx.route = route;

      // Prepares the incoming parameters and data payload before passing it onto the route handler.
      ctx.params  = yield handle.params(ctx.params);
      ctx.payload = yield handle.payload(ctx);

      // Make sure policies and required parameters has been verified.
      yield handle.checkPolicies(ctx, options.policy);
      yield handle.checkParameters(ctx, options.params);

      yield handle.route(ctx, options);
    });
  });
};

/**
 * Runs middleware like operations on the provided route options before executing
 * the request controller.method
 */
let Route = global.Route = Object.assign(koaRouter, {
  resource: require('./resource'),

  post : (endpoint, options) => process('post', endpoint, options),
  get  : (endpoint, options) => process('get', endpoint, options),
  put  : (endpoint, options) => process('put', endpoint, options),
  del  : (endpoint, options) => process('delete', endpoint, options)
});

Route.pst = Route.post;
Route.delete = Route.del;
<|endoftext|>


<commit_before>const slice = Array.prototype.slice;

/**
  Determines if an object is Promise by checking if it is "thenable".
**/
export function isPromise(obj) {
  return (
    ((typeof obj === 'object' && obj !== null) || typeof obj === 'function') &&
    typeof obj.then === 'function'
  );
}

function merge(hash, other) {
  for (let prop in other) {
    if (other.hasOwnProperty(prop)) {
      hash[prop] = other[prop];
    }
  }
}

export let oCreate =
  Object.create ||
  function(proto) {
    function F() {}
    F.prototype = proto;
    return new F();
  };

/**
  @private

  Extracts query params from the end of an array
**/
export function extractQueryParams(array) {
  let len = array && array.length,
    head,
    queryParams;

  if (
    len &&
    len > 0 &&
    array[len - 1] &&
    array[len - 1].hasOwnProperty('queryParams')
  ) {
    queryParams = array[len - 1].queryParams;
    head = slice.call(array, 0, len - 1);
    return [head, queryParams];
  } else {
    return [array, null];
  }
}

/**
  @private

  Coerces query param properties and array elements into strings.
**/
function coerceQueryParamsToString(queryParams) {
  for (let key in queryParams) {
    if (typeof queryParams[key] === 'number') {
      queryParams[key] = '' + queryParams[key];
    } else if (Array.isArray(queryParams[key])) {
      for (let i = 0, l = queryParams[key].length; i < l; i++) {
        queryParams[key][i] = '' + queryParams[key][i];
      }
    }
  }
}
/**
  @private
 */
export function log(router, sequence, msg) {
  if (!router.log) {
    return;
  }

  if (arguments.length === 3) {
    router.log('Transition #' + sequence + ': ' + msg);
  } else {
    msg = sequence;
    router.log(msg);
  }
}

export function bind(context, fn) {
  let boundArgs = arguments;
  return function(value) {
    let args = slice.call(boundArgs, 2);
    args.push(value);
    return fn.apply(context, args);
  };
}

function isParam(object) {
  return (
    typeof object === 'string' ||
    object instanceof String ||
    typeof object === 'number' ||
    object instanceof Number
  );
}

export function forEach(array, callback) {
  for (
    let i = 0, l = array.length;
    i < l && false !== callback(array[i]);
    i++
  ) {
    // empty intentionally
  }
}

export function trigger(router, handlerInfos, ignoreFailure, args) {
  if (router.triggerEvent) {
    router.triggerEvent(handlerInfos, ignoreFailure, args);
    return;
  }

  let name = args.shift();

  if (!handlerInfos) {
    if (ignoreFailure) {
      return;
    }
    throw new Error(
      "Could not trigger event '" + name + "'. There are no active handlers"
    );
  }

  let eventWasHandled = false;

  function delayedEvent(name, args, handler) {
    handler.events[name].apply(handler, args);
  }

  for (let i = handlerInfos.length - 1; i >= 0; i--) {
    let handlerInfo = handlerInfos[i],
      handler = handlerInfo.handler;

    // If there is no handler, it means the handler hasn't resolved yet which
    // means that we should trigger the event later when the handler is available
    if (!handler) {
      handlerInfo.handlerPromise.then(bind(null, delayedEvent, name, args));
      continue;
    }

    if (handler.events && handler.events[name]) {
      if (handler.events[name].apply(handler, args) === true) {
        eventWasHandled = true;
      } else {
        return;
      }
    }
  }

  // In the case that we got an UnrecognizedURLError as an event with no handler,
  // let it bubble up
  if (name === 'error' && args[0].name === 'UnrecognizedURLError') {
    throw args[0];
  } else if (!eventWasHandled && !ignoreFailure) {
    throw new Error("Nothing handled the event '" + name + "'.");
  }
}

export function getChangelist(oldObject, newObject) {
  let key;
  let results = {
    all: {},
    changed: {},
    removed: {},
  };

  merge(results.all, newObject);

  let didChange = false;
  coerceQueryParamsToString(oldObject);
  coerceQueryParamsToString(newObject);

  // Calculate removals
  for (key in oldObject) {
    if (oldObject.hasOwnProperty(key)) {
      if (!newObject.hasOwnProperty(key)) {
        didChange = true;
        results.removed[key] = oldObject[key];
      }
    }
  }

  // Calculate changes
  for (key in newObject) {
    if (newObject.hasOwnProperty(key)) {
      if (Array.isArray(oldObject[key]) && Array.isArray(newObject[key])) {
        if (oldObject[key].length !== newObject[key].length) {
          results.changed[key] = newObject[key];
          didChange = true;
        } else {
          for (let i = 0, l = oldObject[key].length; i < l; i++) {
            if (oldObject[key][i] !== newObject[key][i]) {
              results.changed[key] = newObject[key];
              didChange = true;
            }
          }
        }
      } else {
        if (oldObject[key] !== newObject[key]) {
          results.changed[key] = newObject[key];
          didChange = true;
        }
      }
    }
  }

  return didChange && results;
}

export function promiseLabel(label) {
  return 'Router: ' + label;
}

export function subclass(parentConstructor, proto) {
  function C(props) {
    parentConstructor.call(this, props || {});
  }
  C.prototype = oCreate(parentConstructor.prototype);
  merge(C.prototype, proto);
  return C;
}

function resolveHook(obj, hookName) {
  if (!obj) {
    return;
  }
  let underscored = '_' + hookName;
  return (obj[underscored] && underscored) || (obj[hookName] && hookName);
}

function callHook(obj, _hookName, arg1, arg2) {
  let hookName = resolveHook(obj, _hookName);
  return hookName && obj[hookName].call(obj, arg1, arg2);
}

function applyHook(obj, _hookName, args) {
  let hookName = resolveHook(obj, _hookName);
  if (hookName) {
    if (args.length === 0) {
      return obj[hookName].call(obj);
    } else if (args.length === 1) {
      return obj[hookName].call(obj, args[0]);
    } else if (args.length === 2) {
      return obj[hookName].call(obj, args[0], args[1]);
    } else {
      return obj[hookName].apply(obj, args);
    }
  }
}

export {
  merge,
  slice,
  isParam,
  coerceQueryParamsToString,
  callHook,
  resolveHook,
  applyHook,
};
<commit_msg>Allow functioning with null prototype objects.<commit_after>const slice = Array.prototype.slice;
const hasOwnProperty = Object.prototype.hasOwnProperty;

/**
  Determines if an object is Promise by checking if it is "thenable".
**/
export function isPromise(obj) {
  return (
    ((typeof obj === 'object' && obj !== null) || typeof obj === 'function') &&
    typeof obj.then === 'function'
  );
}

function merge(hash, other) {
  for (let prop in other) {
    if (hasOwnProperty.call(other, prop)) {
      hash[prop] = other[prop];
    }
  }
}

export let oCreate =
  Object.create ||
  function(proto) {
    function F() {}
    F.prototype = proto;
    return new F();
  };

/**
  @private

  Extracts query params from the end of an array
**/
export function extractQueryParams(array) {
  let len = array && array.length,
    head,
    queryParams;

  if (
    len &&
    len > 0 &&
    array[len - 1] &&
    array[len - 1].hasOwnProperty('queryParams')
  ) {
    queryParams = array[len - 1].queryParams;
    head = slice.call(array, 0, len - 1);
    return [head, queryParams];
  } else {
    return [array, null];
  }
}

/**
  @private

  Coerces query param properties and array elements into strings.
**/
function coerceQueryParamsToString(queryParams) {
  for (let key in queryParams) {
    if (typeof queryParams[key] === 'number') {
      queryParams[key] = '' + queryParams[key];
    } else if (Array.isArray(queryParams[key])) {
      for (let i = 0, l = queryParams[key].length; i < l; i++) {
        queryParams[key][i] = '' + queryParams[key][i];
      }
    }
  }
}
/**
  @private
 */
export function log(router, sequence, msg) {
  if (!router.log) {
    return;
  }

  if (arguments.length === 3) {
    router.log('Transition #' + sequence + ': ' + msg);
  } else {
    msg = sequence;
    router.log(msg);
  }
}

export function bind(context, fn) {
  let boundArgs = arguments;
  return function(value) {
    let args = slice.call(boundArgs, 2);
    args.push(value);
    return fn.apply(context, args);
  };
}

function isParam(object) {
  return (
    typeof object === 'string' ||
    object instanceof String ||
    typeof object === 'number' ||
    object instanceof Number
  );
}

export function forEach(array, callback) {
  for (
    let i = 0, l = array.length;
    i < l && false !== callback(array[i]);
    i++
  ) {
    // empty intentionally
  }
}

export function trigger(router, handlerInfos, ignoreFailure, args) {
  if (router.triggerEvent) {
    router.triggerEvent(handlerInfos, ignoreFailure, args);
    return;
  }

  let name = args.shift();

  if (!handlerInfos) {
    if (ignoreFailure) {
      return;
    }
    throw new Error(
      "Could not trigger event '" + name + "'. There are no active handlers"
    );
  }

  let eventWasHandled = false;

  function delayedEvent(name, args, handler) {
    handler.events[name].apply(handler, args);
  }

  for (let i = handlerInfos.length - 1; i >= 0; i--) {
    let handlerInfo = handlerInfos[i],
      handler = handlerInfo.handler;

    // If there is no handler, it means the handler hasn't resolved yet which
    // means that we should trigger the event later when the handler is available
    if (!handler) {
      handlerInfo.handlerPromise.then(bind(null, delayedEvent, name, args));
      continue;
    }

    if (handler.events && handler.events[name]) {
      if (handler.events[name].apply(handler, args) === true) {
        eventWasHandled = true;
      } else {
        return;
      }
    }
  }

  // In the case that we got an UnrecognizedURLError as an event with no handler,
  // let it bubble up
  if (name === 'error' && args[0].name === 'UnrecognizedURLError') {
    throw args[0];
  } else if (!eventWasHandled && !ignoreFailure) {
    throw new Error("Nothing handled the event '" + name + "'.");
  }
}

export function getChangelist(oldObject, newObject) {
  let key;
  let results = {
    all: {},
    changed: {},
    removed: {},
  };

  merge(results.all, newObject);

  let didChange = false;
  coerceQueryParamsToString(oldObject);
  coerceQueryParamsToString(newObject);

  // Calculate removals
  for (key in oldObject) {
    if (hasOwnProperty.call(oldObject, key)) {
      if (!hasOwnProperty.call(newObject, key)) {
        didChange = true;
        results.removed[key] = oldObject[key];
      }
    }
  }

  // Calculate changes
  for (key in newObject) {
    if (hasOwnProperty.call(newObject, key)) {
      if (Array.isArray(oldObject[key]) && Array.isArray(newObject[key])) {
        if (oldObject[key].length !== newObject[key].length) {
          results.changed[key] = newObject[key];
          didChange = true;
        } else {
          for (let i = 0, l = oldObject[key].length; i < l; i++) {
            if (oldObject[key][i] !== newObject[key][i]) {
              results.changed[key] = newObject[key];
              didChange = true;
            }
          }
        }
      } else {
        if (oldObject[key] !== newObject[key]) {
          results.changed[key] = newObject[key];
          didChange = true;
        }
      }
    }
  }

  return didChange && results;
}

export function promiseLabel(label) {
  return 'Router: ' + label;
}

export function subclass(parentConstructor, proto) {
  function C(props) {
    parentConstructor.call(this, props || {});
  }
  C.prototype = oCreate(parentConstructor.prototype);
  merge(C.prototype, proto);
  return C;
}

function resolveHook(obj, hookName) {
  if (!obj) {
    return;
  }
  let underscored = '_' + hookName;
  return (obj[underscored] && underscored) || (obj[hookName] && hookName);
}

function callHook(obj, _hookName, arg1, arg2) {
  let hookName = resolveHook(obj, _hookName);
  return hookName && obj[hookName].call(obj, arg1, arg2);
}

function applyHook(obj, _hookName, args) {
  let hookName = resolveHook(obj, _hookName);
  if (hookName) {
    if (args.length === 0) {
      return obj[hookName].call(obj);
    } else if (args.length === 1) {
      return obj[hookName].call(obj, args[0]);
    } else if (args.length === 2) {
      return obj[hookName].call(obj, args[0], args[1]);
    } else {
      return obj[hookName].apply(obj, args);
    }
  }
}

export {
  merge,
  slice,
  isParam,
  coerceQueryParamsToString,
  callHook,
  resolveHook,
  applyHook,
};
<|endoftext|>


<commit_before>const router = require('express').Router();
const Mood = require('../models/mood');

router
    .get('/moods', (req, res, next) => {
        const date = req.query.date;
        const query = '';
        Mood.find()
            .then(moods => res.send(moods))
            .catch(next);
    })
    .get('/moods/zip/:zipcode', (req, res, next) => {
        const zipcode = req.params.zipcode;
        Mood.find()
            .where('zipcode', zipcode)
            .populate('block color')
            .then(moods => res.send(moods))
            .catch(next);
    })
    .get('/moods/weather/:description', (req, res, next) => {
        const description = req.params.description;
        console.log('description', description);
        Mood.find()
            .where('weather.description', description)
            .populate('block color')
            .then(moods => res.send(moods))
            .catch(next);
    })
    .get('/moods/city/:city', (req, res, next) => {
        const city = req.params.city;
        Mood.find()
            .where('weather.city', city)
            .populate('block color')
            .then(moods => res.send(moods))
            .catch(next);
    })
    .get('/moods/state/:state', (req, res, next) => {
        const state = req.params.state;
        Mood.find()
            .where('weather.state', state)
            .populate('block color')
            .then(moods => res.send(moods))
            .catch(next);
    })
    .get('/moods/country/:country', (req, res, next) => {
        const country = req.params.country;
        Mood.find()
            .where('weather.country', country)
            .populate('block color')
            .then(moods => res.send(moods))
            .catch(next);
    });

module.exports = router;
<commit_msg>mior edit<commit_after>const router = require('express').Router();
const Mood = require('../models/mood');

router
    .get('/moods/', (req, res, next) => {
        Mood.find()
            .populate('block color')
            .then(moods => res.send(moods))
            .catch(next);
    })
    .get('/moods/', (req, res, next) => {
        const date = req.query.date;
        Mood.find()
            .where('date', date)
            .populate('block color')
            .then(moods => res.send(moods))
            .catch(next);
    })
    .get('/moods/zip/', (req, res, next) => {
        const zipcode = req.query.zipcode;
        Mood.find()
            .where('zipcode', zipcode)
            .populate('block color')
            .then(moods => res.send(moods))
            .catch(next);
    })
    .get('/moods/weather/', (req, res, next) => {
        const description = req.query.description;
        console.log('description', description);
        Mood.find()
            .where('weather.description', description)
            .populate('block color')
            .then(moods => res.send(moods))
            .catch(next);
    })
    .get('/moods/city/:city', (req, res, next) => {
        const city = req.params.city;
        Mood.find()
            .where('weather.city', city)
            .populate('block color')
            .then(moods => res.send(moods))
            .catch(next);
    })
    .get('/moods/state/:state', (req, res, next) => {
        const state = req.params.state;
        Mood.find()
            .where('weather.state', state)
            .populate('block color')
            .then(moods => res.send(moods))
            .catch(next);
    })
    .get('/moods/country/:country', (req, res, next) => {
        const country = req.params.country;
        Mood.find()
            .where('weather.country', country)
            .populate('block color')
            .then(moods => res.send(moods))
            .catch(next);
    });

module.exports = router;
<|endoftext|>


<commit_before>const chai = require('chai');
const chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
const should = chai.should();
const { start } = require('../lib');
const { Promise } = require('bluebird');
const delay = Promise.delay;

const spawnChildrenEchoer = (parent, name) => parent.spawnFixed(() => tell(sender, [...children.keys()]), name);
const ignore = () => { };

const retry = async (assertion, remainingAttempts, retryInterval = 0) => {
  if (remainingAttempts <= 1) {
    return assertion();
  } else {
    try {
      assertion();
    } catch (e) {
      await delay(retryInterval);
      await retry(assertion, remainingAttempts - 1, retryInterval);
    }
  }
}

describe('Actor', function () {

  describe('#stop()', function () {

    let system = undefined;
    beforeEach(() => system = start());
    afterEach(() => system.terminate());

    it('stops children when parent is stopped', async function () {
      let actor = spawnChildrenEchoer(system);
      let child1 = spawnChildrenEchoer(actor, 'child1');
      let child2 = spawnChildrenEchoer(actor, 'child2');
      let grandchild1 = child1.spawnFixed(ignore, 'grandchild1');
      let grandchild2 = child1.spawnFixed(ignore, 'grandchild2');

      // Wait for actors to start up correctly
      child1.stop();
      child1.isStopped.should.be.true;
      grandchild1.isStopped.should.be.true;
      grandchild2.isStopped.should.be.true;

      system.stop();
      system.children.should.be.empty;
      actor.isStopped.should.be.true;
      child2.isStopped.should.be.true;
    });

    it('is invoked automatically when a fixed function returns false', async function () {
      let child = system.spawnFixed(() => false, 'testActor');
      child.tell();
      await retry(()=> child.isStopped.should.be.true, 8, 100);
      system.children.should.not.include('testActor');
    });

    it('is invoked automatically when a function is not returned', async function () {
      let child = system.spawn(() => (msg) => { }, 'testActor');
      child.tell();
      await retry(()=> child.isStopped.should.be.true, 8, 100);
      system.children.should.not.include('testActor');
    });

  });

  describe('#terminate()', function () {
    let system = undefined;
    beforeEach(() => system = start());
    afterEach(() => system.terminate());

    it('kills a runaway infinitely looping process', async function () {
      let runAwayActor = system.spawnFixed((msg) => {
        function fibonacci(num) {
          var a = 1, b = 0, temp;
          while (num >= 0) {
            temp = a;
            a = a + b;
            b = temp;
            num--;
          }
          return b;
        }
        let i = 0;
        tell(sender, 'start fib');
        while (true) {
          i++;
          fibonacci(i);
        }
      }, 'testActor');

      await runAwayActor.ask();
      runAwayActor.terminate();
      runAwayActor.isStopped.should.be.true;
    });

    it('terminates children when parent is terminated', async function () {
      let actor = spawnChildrenEchoer(system);
      let child1 = spawnChildrenEchoer(actor, 'child1');
      let child2 = spawnChildrenEchoer(actor, 'child2');
      let grandchild1 = child1.spawnFixed(ignore, 'grandchild1');
      let grandchild2 = child1.spawnFixed(ignore, 'grandchild2');

      child1.terminate();

      child1.isStopped.should.be.true;
      grandchild1.isStopped.should.be.true;
      grandchild2.isStopped.should.be.true;

      system.terminate();
      system.children.should.be.empty;
      actor.isStopped.should.be.true;
      child2.isStopped.should.be.true;
    });

  });

  describe('#spawn()', function () {

    let system = undefined;
    beforeEach(() => system = start());
    afterEach(() => system.terminate());

    it('automatically names an actor if a name is not provided', function () {
      let child = system.spawnFixed((msg) => msg);
      system.children.size.should.equal(1);
      child.name.should.not.be.undefined;
    });

    it('correctly registers children upon startup', async function () {
      let child = spawnChildrenEchoer(system, 'testChildActor');
      system.children.should.have.keys('testChildActor');

      let children = await child.ask();
      children.should.be.empty;

      let grandchild = child.spawnFixed(ignore, 'testGrandchildActor');
      children = await child.ask();

      children.should.have.members(['testGrandchildActor']);
      child.children.should.have.keys('testGrandchildActor');

      let grandchild2 = child.spawnFixed(ignore, 'testGrandchildActor2');
      children = await child.ask();
      child.children.should.have.keys('testGrandchildActor2', 'testGrandchildActor');
      children.should.have.members(['testGrandchildActor2', 'testGrandchildActor']);
    });

    it('can be invoked from within actor', async function () {
      let actor = system.spawnFixed(async (msg) => {
        if (msg === 'spawn') {
          try {
            await spawnFixed((msg) => { }, 'child1');//.then(result => tell(sender, result));
          } catch (e) {
            console.log(e.message);
          }
        } else {
          tell(sender, [...children.keys()]);
        }
      }, 'test');
      actor.tell('spawn');
      let children = await actor.ask('query');
      children.should.have.members(['child1']);
      actor.children.should.have.keys('child1');
    }).timeout(6000);
  });

  describe('#ask()', function () {
    let system = undefined;
    beforeEach(() => system = start());
    afterEach(() => system.terminate());

    it(`should reject a promise if the actor hasn't responded with the given timespan`, function () {

      let actor = system.spawnFixed(
        async (msg) => { await delay(10); tell(sender, 'done'); },
        'test',
        { delay: { f: (actor, length) => delay(length), async: true } }
      );

      return actor.ask('test', 1).should.be.rejectedWith(Error, 'Ask Timeout');

    });
  });

  describe('#tell()', function () {
    let system = undefined;
    beforeEach(() => system = start());
    afterEach(() => system.terminate());

    it('should allow statful behaviour via trampolining', async function () {
      let actor = system.spawn(() => {
        let initialState = '';
        let f = (state) => (msg) => {
          if (msg.type === 'query') {
            tell(sender, state);
            return f(state);
          } else if (msg.type === 'append') {
            return f(state + msg.payload);
          }
        };
        return f(initialState);
      });

      actor.tell({ payload: 'Hello ', type: 'append' });
      actor.tell({ payload: 'World. ', type: 'append' });
      actor.tell({ payload: 'The time has come!!', type: 'append' });
      let result = await actor.ask({ type: 'query' });
      result.should.equal('Hello World. The time has come!!');
    });

    it('should be able to message other actors', async function () {
      let child1 = system.spawnFixed((msg) => tell(msg, sender));
      let child2 = system.spawnFixed((msg) => tell(msg, 'hello from child2'));
      let result = await child1.ask(child2.path);
      result.should.equal('hello from child2');
    });

  });

  describe('#<effect>()', function () {

    let system = undefined;
    beforeEach(() => system = start());
    afterEach(() => system.terminate());

    it('can be resolved when returning a value', async function () {
      let child = system.spawnFixed(async (msg) => {
        let result = await getMockValue();
        tell(sender, result);
      }, 'testActor', { getMockValue: { f: () => 2, async: true } });

      let result = await child.ask();
      result.should.equal(2);
    });

    it('can be resolved when returning a promise', async function () {
      let child = system.spawnFixed(async (msg) => {
        let result = await getMockValue();
        tell(sender, result);
      }, 'testActor', { getMockValue: { f: () => Promise.resolve(2), async: true } });

      let result = await child.ask();
      result.should.equal(2);
    });

    it('is evaluted in order when returning a promise from the actor function', async function () {
      let child = system.spawnFixed(async (msg) => {
        if (msg === 2) {
          await delayExecution(50);
        }
        tell(sender, msg);
      }, 'testActor', { delayExecution: { f: (d) => delay(d), async: true } });

      let result1 = await child.ask(1);
      let result2 = await child.ask(2);
      let result3 = await child.ask(3);
      result1.should.equal(1);
      result2.should.equal(2);
      result3.should.equal(3);
    });

  });

});<commit_msg>Made tests a bit more lenient<commit_after>const chai = require('chai');
const chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
const should = chai.should();
const { start } = require('../lib');
const { Promise } = require('bluebird');
const delay = Promise.delay;

const spawnChildrenEchoer = (parent, name) => parent.spawnFixed(() => tell(sender, [...children.keys()]), name);
const ignore = () => { };

const retry = async (assertion, remainingAttempts, retryInterval = 0) => {
  if (remainingAttempts <= 1) {
    return assertion();
  } else {
    try {
      assertion();
    } catch (e) {
      await delay(retryInterval);
      await retry(assertion, remainingAttempts - 1, retryInterval);
    }
  }
}

describe('Actor', function () {

  describe('#stop()', function () {

    let system = undefined;
    beforeEach(() => system = start());
    afterEach(() => system.terminate());

    it('stops children when parent is stopped', async function () {
      let actor = spawnChildrenEchoer(system);
      let child1 = spawnChildrenEchoer(actor, 'child1');
      let child2 = spawnChildrenEchoer(actor, 'child2');
      let grandchild1 = child1.spawnFixed(ignore, 'grandchild1');
      let grandchild2 = child1.spawnFixed(ignore, 'grandchild2');

      // Wait for actors to start up correctly
      child1.stop();
      child1.isStopped.should.be.true;
      grandchild1.isStopped.should.be.true;
      grandchild2.isStopped.should.be.true;

      system.stop();
      system.children.should.be.empty;
      actor.isStopped.should.be.true;
      child2.isStopped.should.be.true;
    });

    it('is invoked automatically when a fixed function returns false', async function () {
      let child = system.spawnFixed(() => false, 'testActor');
      child.tell();
      await retry(()=> child.isStopped.should.be.true, 12, 100);
      system.children.should.not.include('testActor');
    });

    it('is invoked automatically when a function is not returned', async function () {
      let child = system.spawn(() => (msg) => { }, 'testActor');
      child.tell();
      await retry(()=> child.isStopped.should.be.true, 12, 100);
      system.children.should.not.include('testActor');
    });

  });

  describe('#terminate()', function () {
    let system = undefined;
    beforeEach(() => system = start());
    afterEach(() => system.terminate());

    it('kills a runaway infinitely looping process', async function () {
      let runAwayActor = system.spawnFixed((msg) => {
        function fibonacci(num) {
          var a = 1, b = 0, temp;
          while (num >= 0) {
            temp = a;
            a = a + b;
            b = temp;
            num--;
          }
          return b;
        }
        let i = 0;
        tell(sender, 'start fib');
        while (true) {
          i++;
          fibonacci(i);
        }
      }, 'testActor');

      await runAwayActor.ask();
      runAwayActor.terminate();
      runAwayActor.isStopped.should.be.true;
    });

    it('terminates children when parent is terminated', async function () {
      let actor = spawnChildrenEchoer(system);
      let child1 = spawnChildrenEchoer(actor, 'child1');
      let child2 = spawnChildrenEchoer(actor, 'child2');
      let grandchild1 = child1.spawnFixed(ignore, 'grandchild1');
      let grandchild2 = child1.spawnFixed(ignore, 'grandchild2');

      child1.terminate();

      child1.isStopped.should.be.true;
      grandchild1.isStopped.should.be.true;
      grandchild2.isStopped.should.be.true;

      system.terminate();
      system.children.should.be.empty;
      actor.isStopped.should.be.true;
      child2.isStopped.should.be.true;
    });

  });

  describe('#spawn()', function () {

    let system = undefined;
    beforeEach(() => system = start());
    afterEach(() => system.terminate());

    it('automatically names an actor if a name is not provided', function () {
      let child = system.spawnFixed((msg) => msg);
      system.children.size.should.equal(1);
      child.name.should.not.be.undefined;
    });

    it('correctly registers children upon startup', async function () {
      let child = spawnChildrenEchoer(system, 'testChildActor');
      system.children.should.have.keys('testChildActor');

      let children = await child.ask();
      children.should.be.empty;

      let grandchild = child.spawnFixed(ignore, 'testGrandchildActor');
      children = await child.ask();

      children.should.have.members(['testGrandchildActor']);
      child.children.should.have.keys('testGrandchildActor');

      let grandchild2 = child.spawnFixed(ignore, 'testGrandchildActor2');
      children = await child.ask();
      child.children.should.have.keys('testGrandchildActor2', 'testGrandchildActor');
      children.should.have.members(['testGrandchildActor2', 'testGrandchildActor']);
    });

    it('can be invoked from within actor', async function () {
      let actor = system.spawnFixed(async (msg) => {
        if (msg === 'spawn') {
          try {
            await spawnFixed((msg) => { }, 'child1');//.then(result => tell(sender, result));
          } catch (e) {
            console.log(e.message);
          }
        } else {
          tell(sender, [...children.keys()]);
        }
      }, 'test');
      actor.tell('spawn');
      let children = await actor.ask('query');
      children.should.have.members(['child1']);
      actor.children.should.have.keys('child1');
    }).timeout(6000);
  });

  describe('#ask()', function () {
    let system = undefined;
    beforeEach(() => system = start());
    afterEach(() => system.terminate());

    it(`should reject a promise if the actor hasn't responded with the given timespan`, function () {

      let actor = system.spawnFixed(
        async (msg) => { await delay(10); tell(sender, 'done'); },
        'test',
        { delay: { f: (actor, length) => delay(length), async: true } }
      );

      return actor.ask('test', 1).should.be.rejectedWith(Error, 'Ask Timeout');

    });
  });

  describe('#tell()', function () {
    let system = undefined;
    beforeEach(() => system = start());
    afterEach(() => system.terminate());

    it('should allow statful behaviour via trampolining', async function () {
      let actor = system.spawn(() => {
        let initialState = '';
        let f = (state) => (msg) => {
          if (msg.type === 'query') {
            tell(sender, state);
            return f(state);
          } else if (msg.type === 'append') {
            return f(state + msg.payload);
          }
        };
        return f(initialState);
      });

      actor.tell({ payload: 'Hello ', type: 'append' });
      actor.tell({ payload: 'World. ', type: 'append' });
      actor.tell({ payload: 'The time has come!!', type: 'append' });
      let result = await actor.ask({ type: 'query' });
      result.should.equal('Hello World. The time has come!!');
    });

    it('should be able to message other actors', async function () {
      let child1 = system.spawnFixed((msg) => tell(msg, sender));
      let child2 = system.spawnFixed((msg) => tell(msg, 'hello from child2'));
      let result = await child1.ask(child2.path);
      result.should.equal('hello from child2');
    });

  });

  describe('#<effect>()', function () {

    let system = undefined;
    beforeEach(() => system = start());
    afterEach(() => system.terminate());

    it('can be resolved when returning a value', async function () {
      let child = system.spawnFixed(async (msg) => {
        let result = await getMockValue();
        tell(sender, result);
      }, 'testActor', { getMockValue: { f: () => 2, async: true } });

      let result = await child.ask();
      result.should.equal(2);
    });

    it('can be resolved when returning a promise', async function () {
      let child = system.spawnFixed(async (msg) => {
        let result = await getMockValue();
        tell(sender, result);
      }, 'testActor', { getMockValue: { f: () => Promise.resolve(2), async: true } });

      let result = await child.ask();
      result.should.equal(2);
    });

    it('is evaluted in order when returning a promise from the actor function', async function () {
      let child = system.spawnFixed(async (msg) => {
        if (msg === 2) {
          await delayExecution(50);
        }
        tell(sender, msg);
      }, 'testActor', { delayExecution: { f: (d) => delay(d), async: true } });

      let result1 = await child.ask(1);
      let result2 = await child.ask(2);
      let result3 = await child.ask(3);
      result1.should.equal(1);
      result2.should.equal(2);
      result3.should.equal(3);
    });

  });

});<|endoftext|>


<commit_before>/**
 *
Copyright (c) 2012 by Appcelerator, Inc. All Rights Reserved.
 * Please see the LICENSE file for information about licensing.

 *
 * @author Bryan Hughes <bhughes@appcelerator.com>
 */

var path = require("path"),
	RuleProcessor = require("../RuleProcessor"),
	Base = require("../Base");

exports.processRule = function(ast) {
	RuleProcessor.fireRuleEvent(ast, {}, false);

	var operator = ast[1],
		leftValue = Base.getValue(RuleProcessor.processRule(ast[2])),
		leftType = Base.type(leftValue),
		rightValue = Base.getValue(RuleProcessor.processRule(ast[3])),
		rightType = Base.type(rightValue),
		result;

	if (leftType === "Unknown" || rightType === "Unknown") {
		result = new Base.UnknownType();
	} else if (~["*", "/", "%", "-"].indexOf(operator)) {
		result = new Base.NumberType();
		switch(operator) {
			case "*":
				result.value = Base.toNumber(leftValue).value * Base.toNumber(rightValue).value;
				break;
			case "/":
				result.value = Base.toNumber(leftValue).value / Base.toNumber(rightValue).value;
				break;
			case "%":
				result.value = Base.toNumber(leftValue).value % Base.toNumber(rightValue).value;
				break;
			case "-":
				result.value = Base.toNumber(leftValue).value - Base.toNumber(rightValue).value;
				break;
		}
	} else if (operator === "+") {
		var leftPrimitive = Base.toPrimitive(leftValue),
			rightPrimitive = Base.toPrimitive(rightValue);

		if (Base.type(leftPrimitive) === "String" || Base.type(rightPrimitive) === "String") {
			result = new Base.StringType();
			result.value = Base.toString(leftValue).value + Base.toString(rightValue).value;
		} else {
			result = new Base.NumberType();
			result.value = Base.toNumber(leftValue).value + Base.toNumber(rightValue).value;
		}
	}

	// TODO:
	// << >> >>>
	// < > <= >= instanceof in
	// == != === !==
	// & | ^
	// && ||

	RuleProcessor.fireRuleEvent(ast, {}, true);
	return result;
};
RuleProcessor.registerRuleProcessor(path.basename(__filename, ".js"), exports);<commit_msg>[TIMOB-9075] Finished implementing the binary expression operator.<commit_after>/**
 *
Copyright (c) 2012 by Appcelerator, Inc. All Rights Reserved.
 * Please see the LICENSE file for information about licensing.

 *
 * @author Bryan Hughes <bhughes@appcelerator.com>
 */

var path = require("path"),
	RuleProcessor = require("../RuleProcessor"),
	Base = require("../Base");

exports.processRule = function(ast) {
	RuleProcessor.fireRuleEvent(ast, {}, false);

	var operator = ast[1],
		leftValue = Base.getValue(RuleProcessor.processRule(ast[2])),
		leftType = Base.type(leftValue),
		rightValue = Base.getValue(RuleProcessor.processRule(ast[3])),
		rightType = Base.type(rightValue),
		result;

	if (leftType === "Unknown" || rightType === "Unknown") {
		result = new Base.UnknownType();
	} else if (~["*", "/", "%", "-", "<<", ">>", ">>>", "&", "|", "^"].indexOf(operator)) {
		result = new Base.NumberType();
		switch(operator) {
			case "*":
				result.value = Base.toNumber(leftValue).value * Base.toNumber(rightValue).value;
				break;
			case "/":
				result.value = Base.toNumber(leftValue).value / Base.toNumber(rightValue).value;
				break;
			case "%":
				result.value = Base.toNumber(leftValue).value % Base.toNumber(rightValue).value;
				break;
			case "-":
				result.value = Base.toNumber(leftValue).value - Base.toNumber(rightValue).value;
				break;
			case "<<":
				result.value = Base.toInt32(leftValue).value << (Base.toUint32(rightValue).value & 0x1F);
				break;
			case ">>":
				result.value = Base.toInt32(leftValue).value >> (Base.toUint32(rightValue).value & 0x1F);
				break;
			case ">>>":
				result.value = Base.toUint32(leftValue).value >>> (Base.toUint32(rightValue).value & 0x1F);
				break;
			case "&":
				result.value = Base.toInt32(leftValue).value & (Base.toInt32(rightValue).value);
				break;
			case "|":
				result.value = Base.toInt32(leftValue).value | (Base.toInt32(rightValue).value);
				break;
			case "^":
				result.value = Base.toInt32(leftValue).value ^ (Base.toInt32(rightValue).value);
				break;
		}
	} else if (operator === "+") {
		var leftPrimitive = Base.toPrimitive(leftValue),
			rightPrimitive = Base.toPrimitive(rightValue);

		if (Base.type(leftPrimitive) === "String" || Base.type(rightPrimitive) === "String") {
			result = new Base.StringType();
			result.value = Base.toString(leftValue).value + Base.toString(rightValue).value;
		} else {
			result = new Base.NumberType();
			result.value = Base.toNumber(leftValue).value + Base.toNumber(rightValue).value;
		}
	} else if (~["<", ">", "<=", ">=", "instanceof", "in", "==", "===", "!=", "!=="].indexOf(operator)) {

		var leftPrimitive = Base.toPrimitive(leftValue),
			rightPrimitive = Base.toPrimitive(rightValue);
		result = new Base.BooleanType();
		switch(operator) {
			case "<":
				result.value = leftPrimitive.value < rightPrimitive.value;
				break;
			case ">":
				result.value = leftPrimitive.value > rightPrimitive.value;
				break;
			case "<=":
				result.value = leftPrimitive.value <= rightPrimitive.value;
				break;
			case ">=":
				result.value = leftPrimitive.value >= rightPrimitive.value;
				break;
			case "isntanceof":
				if (Base.type(rightValue) !== "Object" || !rightValue.hasInstance) {
					throw new Exceptions.TypeError();
				}
				result.value = rightValue.hasInstance(leftValue);
				break;
			case "in":
				if (Base.type(rightValue) !== "Object") {
					throw new Exceptions.TypeError();
				}
				result.value = rightValue.hasProperty(Base.toString(leftValue).value);
				break;
			case "==":
				result.value = leftPrimitive.value == rightPrimitive.value;
				break;
			case "===":
				result.value = leftPrimitive.value === rightPrimitive.value;
				break;
			case "!=":
				result.value = leftPrimitive.value != rightPrimitive.value;
				break;
			case "!==":
				result.value = leftPrimitive.value !== rightPrimitive.value;
				break;
		}
	} else if (operator === "&&") {
		if (!Base.toBoolean(leftValue).value) {
			result = leftValue;
		} else {
			result = rightValue;
		}
	} else if (operator === "||") {
		if (Base.toBoolean(leftValue).value) {
			result = leftValue;
		} else {
			result = rightValue;
		}
	} else {
		throw new Error("Internal Error: An unexpected operator '" + operator + "' was encountered in a binary expression.");
	}

	RuleProcessor.fireRuleEvent(ast, {}, true);
	return result;
};
RuleProcessor.registerRuleProcessor(path.basename(__filename, ".js"), exports);<|endoftext|>


<commit_before>require('setimmediate')
var test = require('tape')

var MutantArray = require('../array')
var MutantValue = require('../value')

test('Observable returns contents when called ', function(t) {
  var array = MutantArray(['cat'])
  var actual = array()
  t.deepEqual(actual, ['cat'])
  t.end()
})

test('Observable calls subscribe with latest when updated with set', function(t) {
  var expected = ['dog']
  var array = MutantArray(['cat'])
  array(actual => {
    t.deepEqual(actual, expected)
    t.end()
  })
  array.set(expected)
})

test('Observable calls multiple subscribers with latest when updated with set', function(t) {
  t.plan(2)
  var expected = ['dog']
  var array = MutantArray(['cat'])
  array(actual => {
    t.deepEqual(actual, expected)
  })
  array(actual => {
    t.deepEqual(actual, expected)
  })
  array.set(expected)
})

test('#push inserts a new element and calls subscriber', function(t) {
  var expected = ['cat','dog']
  var array = MutantArray(['cat'])
  array(actual => {
    t.deepEqual(actual, expected)
    t.end()
  })
  array.push('dog')
})

test('#insert inserts element at index and calls subscriber', function(t) {
  var expected = ['dog', 'cat']
  var array = MutantArray(['cat'])
  array(actual => {
    t.deepEqual(actual, expected)
    t.end()
  })
  array.insert('dog', 0)
})

test('Array will call subscriber when an observable in the array is updated', function(t) {
  var obs = MutantValue(1)
  var array = MutantArray([obs])
  array(arr => {
    actual = arr[0]
    t.equal(actual, 2)
    t.end()
  })
  obs.set(2)
})
<commit_msg>Add test for delete from array.<commit_after>require('setimmediate')
var test = require('tape')

var MutantArray = require('../array')
var MutantValue = require('../value')

test('Observable returns contents when called ', function(t) {
  var array = MutantArray(['cat'])
  var actual = array()
  t.deepEqual(actual, ['cat'])
  t.end()
})

test('Observable calls subscribe with latest when updated with set', function(t) {
  var expected = ['dog']
  var array = MutantArray(['cat'])
  array(actual => {
    t.deepEqual(actual, expected)
    t.end()
  })
  array.set(expected)
})

test('Observable calls multiple subscribers with latest when updated with set', function(t) {
  t.plan(2)
  var expected = ['dog']
  var array = MutantArray(['cat'])
  array(actual => {
    t.deepEqual(actual, expected)
  })
  array(actual => {
    t.deepEqual(actual, expected)
  })
  array.set(expected)
})

test('#push inserts a new element and calls subscriber', function(t) {
  var expected = ['cat','dog']
  var array = MutantArray(['cat'])
  array(actual => {
    t.deepEqual(actual, expected)
    t.end()
  })
  array.push('dog')
})

test('#insert inserts element at index and calls subscriber', function(t) {
  var expected = ['dog', 'cat']
  var array = MutantArray(['cat'])
  array(actual => {
    t.deepEqual(actual, expected)
    t.end()
  })
  array.insert('dog', 0)
})

test('#delete removes element and calls subscriber', function(t) {
  var expected = ['dog']
  var array = MutantArray(['cat', 'dog'])
  array(actual => {
    t.deepEqual(actual, expected)
    t.end()
  })
  array.delete('cat')
})

test('Array will call subscriber when an observable in the array is updated', function(t) {
  var obs = MutantValue(1)
  var array = MutantArray([obs])
  array(arr => {
    actual = arr[0]
    t.equal(actual, 2)
    t.end()
  })
  obs.set(2)
})
<|endoftext|>