<commit_before>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You 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 fs = require('fs');
var sys = require('sys');
var path = require('path');
var events = require('events');

var sprintf = require('sprintf').sprintf;
var async = require('async');

var errors = require('./errors');

/**
 * Resources for which jobs can be queued and executed. Implementations must
 * specify an 'exists' method.
 * @extends {events.EventEmitter}
 * @param {String} name The name of this resource's directory.
 */
function Resource(name) {
  this.name = name;
  this._serializer = undefined;
  this._jobs = [];
  this._waiters = [];
  this._running = null;
  this._refCount = 0;
  this._state = null;
}

sys.inherits(Resource, events.EventEmitter);

/**
 * Retrieve a list of resources of this type.
 * @param {Function} callback A callback fired with (err, list).
 */
Resource.prototype.list = function(callback) { callback(new Error('Not Implemented')); };

/**
 * Determine whether this resource exists. Should be overridden by resource
 * implementations.
 * @param {Function} callback A callback fired with (exists).
 */
Resource.prototype.exists = function(callback) { callback(false); };

/**
 * Determine the swiz type for this resource. Implementations should specify
 * a _serializerType property.
 * @returns {String} The serializer type for use by swiz.
 */
Resource.prototype.getSerializerType = function() {
  return this.constructor.name;
};

/**
 * Retrieve the serialized state of this resource. If a job is currently
 * executing on this resource this will use the state cached when that job
 * began. Otherwise it will retrieve the state on demand.
 * @param {Function} callback A callback fired with (err, state).
 */
Resource.prototype.getState = function(callback) {
  var self = this;

  if (this._running) {
    // If a job is running...
    if (!this._state) {
      // The state isn't ready yet, wait for it...
      this.once('cache', callback);
    } else if (this._state instanceof Error) {
      // The state was an error, whoops
      callback(this._state);
    } else {
      // Use the cached state directly
      callback(null, this._state);
    }
  } else {
    // Make sure no jobs start while we're doing this
    this._refCount++;
    this._waitToCheck(function(afterCheck) {
      self.exists(function(exists) {
        if (!exists) {
          callback(self.getExistenceError(exists));
          self.deref();
          afterCheck();
          return;
        }
        self._serializer.buildObject(self, function(err, state) {
          callback(err, state);
          self.deref();
          afterCheck();
        });
      });
    });
  }
};

/**
 * Attempt to predict whether a resource will exist after the current job
 * queue has been flushed.
 * @param {Function} callback A callback fired with (willExist).
 */
Resource.prototype.willExist = function(callback) {
  var i;

  // Make a copy of the job queue and reverse it
  var priorJobs = this._jobs.slice(0, this._jobs.length).reverse();

  if (this._running) {
    priorJobs.push(this._running);
  }

  // Traverse prior jobs to see if they create/delete the resource
  for (i = 0; i < priorJobs.length; i++) {
    if (priorJobs[i].willCreate()) {
      callback(true);
      return;
    }

    if (priorJobs[i].willDelete()) {
      callback(false);
      return;
    }
  }

  // No job will create or delete this resource, does it exist now?
  this.exists(callback);
};

/**
 * Enqueue a job for this resource.
 * @param {Object} job The job to enqueue.
 */
Resource.prototype.enqueueJob = function(job, callback) {
  var self = this;
  this._refCount++;

  this._waitToCheck(function(afterCheck) {

    // Test whether the resource will exist after the queue flushes.
    self.willExist(function(willExist) {

      // If the job can't handle a resource with willExist, callback with error
      if (!job.canHandleExistence(willExist)) {
        callback(self.getExistenceError(willExist));
        afterCheck();
        self.deref();
        return;
      }

      // When the job ends, start the next one
      job.on('error', function() {
        self._deref(true);
      });

      job.on('success', function() {
        self._deref(true);
      });

      // Enqueue this job and make sure the queue is being processed
      self._jobs.push(job);
      self._cycle();
      afterCheck();
      callback();
    });
  });
};

/**
 * Generate an error for this resource based on the passed boolean indicating
 * what the problem is.
 * @param {Boolean} exists Is that problem that it does exist, or doesn't?
 * @returns {Error} The appropriate error.
 */
Resource.prototype.getExistenceError = function(exists) {
  if (exists) {
    return new errors.AlreadyExistsError(this.constructor.name, this.name);
  } else {
    return new errors.NotFoundError(this.constructor.name, this.name);
  }
};

Resource.prototype.deref = function() {
  this._deref();
};

/**
 * Remove a job by decreasing the reference counter and, if the job was queued,
 * removing the reference to it as well.
 * @param {Boolean} queued Whether the job was queued.
 */
Resource.prototype._deref = function(queued) {
  this._refCount--;

  // Jobs that have reached the queue will always make it to the head
  if (queued) {
    this._running = null;
    this._state = null;
    this._cycle();
  }

  if (this._refCount === 0) {
    this.emit('drain');
  }
};

/**
 * If no job is currently being processed, start the one at the head of the
 * queue.
 * @private
 */
Resource.prototype._cycle = function() {
  var self = this;

  if (!this._running && this._jobs.length > 0) {
    this._running = this._jobs.shift();

    this._serializer.buildObject(this, function(err, state) {
      self._state = err || state;
      self.emit('cache', err, state);
      self._running.start();
    });
  }
};

/**
 * Called by jobs waiting to check their options. Only one job may be checking
 * its options at any given time.
 * @param {Function} callback A callback fired when job may check its options.
 */
Resource.prototype._waitToCheck = function(callback) {
  var self = this;

  function afterCheck() {
    self._waiters.shift();
    if (self._waiters.length > 0) {
      self._waiters[0](afterCheck);
    }
  }

  this._waiters.push(callback);
  if (this._waiters.length === 1) {
    this._waiters[0](afterCheck);
  }
};

/**
 * A resource that is backed by a directory.
 * @param {String} name The name of this resource's directory.
 * @constructor
 */
function DirectoryResource(name) {
  Resource.call(this, name);
  this.parentDir = this.getParentDir();
}

sys.inherits(DirectoryResource, Resource);

/**
 * Get parent directory of resources of this type. This must be set by
 * DirectoryResource implementations.
 * @returns {String} The path to the directory containing resource sof this
 *     type.
 */
DirectoryResource.prototype.getParentDir = function() { return undefined; };

/**
 * @inheritdoc
 */
DirectoryResource.prototype.list = function(callback) {
  fs.readdir(this.getParentDir(), callback);
};

/**
 * Get the path to the root of this resource.
 * @returns {String} Path to the root of this resource.
 */
DirectoryResource.prototype.getRoot = function() {
  return path.join(this.parentDir, this.name);
};

/**
 * Determine whether this resource exists by checking for its root directory.
 * @param {Function} callback A callback fired with (exists).
 */
DirectoryResource.prototype.exists = function(callback) {
  fs.stat(this.getRoot(), function(err, stats) {
    callback(!err && stats.isDirectory());
  });
};

/**
 * A ResourceManager manages access to resources in order to enforce locking,
 * queueing, etc. When a request to access a resource is received, the manager
 * will automatically construct resource objects as necessary, then deconstruct
 * them when they are no longer in use. The resource objects only provide
 * mechanisms for accessing the data they represent, and shouldn't try to
 * maintain resource state in memory.
 * @constructor
 */
function ResourceManager() {
  this.resourceType = undefined;
  this.resources = {};
}

/**
 * Retrieve, if necessary by instantiating, a resource with the specified name.
 * When a resource is instantiated it "job count" will be 0, but it won't be
 * removed until something causes it to emit a 'drain'. Thereore, if you are
 * going to call this you should make sure to kick off something that will
 * increment and later decrement the job counter.
 * @param {String} name The name of this job.
 */
ResourceManager.prototype._getOrCreate = function(name) {
  var self = this;

  // If necessary, instantiate the resource
  if (!this.resources[name]) {
    this.resources[name] = new this.resourceType(name);

    this.resources[name].on('drain', function() {
      delete self.resources[name];
    });
  }

  return this.resources[name];
};

/**
 * Execute the given job against the resource identified by the specified name.
 * @param {Object} job The job to execute.
 */
ResourceManager.prototype.runJob = function(job) {
  var self = this;
  var name = job.resourceName;

  job.enqueueFor(this._getOrCreate(name));
};

/**
 * Retrieve the serialized state of an instance.
 * @param {String} name The name of the resource to retreieve.
 * @param {Function} callback A callback fired with (err, state).
 */
ResourceManager.prototype.get = function(name, callback) {
  var self = this;
  var resource = this._getOrCreate(name);
  resource.getState(callback);
};

/**
 * Get a serialized copy of each resource.
 * @param {Function} callback A callback fired with (err, states).
 */
ResourceManager.prototype.list = function(callback) {
  var self = this;

  function getNames(callback) {
    self.resourceType.prototype.list(callback);
  }

  function mapToStates(names, callback) {
    var resources = names.map(function(name) {
      return self._getOrCreate(name);
    });

    async.map(resources, function(resource, callback) {
      resource.getState(callback);
    },
    function(err, requests) {
      if (!err) {
        requests.sort(function(a, b) {
          return (a.name > b.name);
        });
      }
      callback(err, requests);
    });
  }

  async.waterfall([getNames, mapToStates], callback);
};

/**
 * Resource managers may optionally override this to do asynchronous
 * initialization tasks.
 * @param {Function} callback A callback fired with (err).
 */
ResourceManager.prototype.init = function(callback) {
  callback();
};

exports.ResourceManager = ResourceManager;
exports.DirectoryResource = DirectoryResource;
<commit_msg>add a ResourceManager method to get a "live" resource.<commit_after>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You 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 fs = require('fs');
var sys = require('sys');
var path = require('path');
var events = require('events');

var sprintf = require('sprintf').sprintf;
var async = require('async');

var errors = require('./errors');

/**
 * Resources for which jobs can be queued and executed. Implementations must
 * specify an 'exists' method.
 * @extends {events.EventEmitter}
 * @param {String} name The name of this resource's directory.
 */
function Resource(name) {
  this.name = name;
  this._serializer = undefined;
  this._jobs = [];
  this._waiters = [];
  this._running = null;
  this._refCount = 0;
  this._state = null;
}

sys.inherits(Resource, events.EventEmitter);

/**
 * Retrieve a list of resources of this type.
 * @param {Function} callback A callback fired with (err, list).
 */
Resource.prototype.list = function(callback) { callback(new Error('Not Implemented')); };

/**
 * Determine whether this resource exists. Should be overridden by resource
 * implementations.
 * @param {Function} callback A callback fired with (exists).
 */
Resource.prototype.exists = function(callback) { callback(false); };

/**
 * Determine the swiz type for this resource. Implementations should specify
 * a _serializerType property.
 * @returns {String} The serializer type for use by swiz.
 */
Resource.prototype.getSerializerType = function() {
  return this.constructor.name;
};

/**
 * Perform a "synchronized" check of whether a resource exists. This will
 * _waitToCheck then perform an existence check. If there was no existence
 * error then the caller gets back afterCheck from the callback and is
 * responsible for calling deref(), otherwise afterCheck and deref will be
 * called before the callback is fired. TODO: This needs a better name.
 * @param {Function} callback A callback fired with (err, afterCheck).
 */
Resource.prototype.alive = function(callback) {
  var self = this;

  this._refCount++;
  this._waitToCheck(function(afterCheck) {
    self.exists(function(exists) {
      if (!exists) {
        callback(self.getExistenceError(exists));
        self.deref();
        afterCheck();
      } else {
        callback(null, afterCheck);
      }
    });
  });
};

/**
 * Retrieve the serialized state of this resource. If a job is currently
 * executing on this resource this will use the state cached when that job
 * began. Otherwise it will retrieve the state on demand.
 * @param {Function} callback A callback fired with (err, state).
 */
Resource.prototype.getState = function(callback) {
  var self = this;

  if (this._running) {
    // If a job is running...
    if (!this._state) {
      // The state isn't ready yet, wait for it...
      this.once('cache', callback);
    } else if (this._state instanceof Error) {
      // The state was an error, whoops
      callback(this._state);
    } else {
      // Use the cached state directly
      callback(null, this._state);
    }
  } else {
    // Make sure no jobs start while we're doing this
    self.alive(function(err, afterCheck) {
      if (err) {
        callback(err);
      } else {
        self._serializer.buildObject(self, function(err, state) {
          callback(err, state);
          self.deref();
          afterCheck();
        });
      }
    });
  }
};

/**
 * Attempt to predict whether a resource will exist after the current job
 * queue has been flushed.
 * @param {Function} callback A callback fired with (willExist).
 */
Resource.prototype.willExist = function(callback) {
  var i;

  // Make a copy of the job queue and reverse it
  var priorJobs = this._jobs.slice(0, this._jobs.length).reverse();

  if (this._running) {
    priorJobs.push(this._running);
  }

  // Traverse prior jobs to see if they create/delete the resource
  for (i = 0; i < priorJobs.length; i++) {
    if (priorJobs[i].willCreate()) {
      callback(true);
      return;
    }

    if (priorJobs[i].willDelete()) {
      callback(false);
      return;
    }
  }

  // No job will create or delete this resource, does it exist now?
  this.exists(callback);
};

/**
 * Enqueue a job for this resource.
 * @param {Object} job The job to enqueue.
 */
Resource.prototype.enqueueJob = function(job, callback) {
  var self = this;
  this._refCount++;

  this._waitToCheck(function(afterCheck) {

    // Test whether the resource will exist after the queue flushes.
    self.willExist(function(willExist) {

      // If the job can't handle a resource with willExist, callback with error
      if (!job.canHandleExistence(willExist)) {
        callback(self.getExistenceError(willExist));
        afterCheck();
        self.deref();
        return;
      }

      // When the job ends, start the next one
      job.on('error', function() {
        self._deref(true);
      });

      job.on('success', function() {
        self._deref(true);
      });

      // Enqueue this job and make sure the queue is being processed
      self._jobs.push(job);
      self._cycle();
      afterCheck();
      callback();
    });
  });
};

/**
 * Generate an error for this resource based on the passed boolean indicating
 * what the problem is.
 * @param {Boolean} exists Is that problem that it does exist, or doesn't?
 * @returns {Error} The appropriate error.
 */
Resource.prototype.getExistenceError = function(exists) {
  if (exists) {
    return new errors.AlreadyExistsError(this.constructor.name, this.name);
  } else {
    return new errors.NotFoundError(this.constructor.name, this.name);
  }
};

Resource.prototype.deref = function() {
  this._deref();
};

/**
 * Remove a job by decreasing the reference counter and, if the job was queued,
 * removing the reference to it as well.
 * @param {Boolean} queued Whether the job was queued.
 */
Resource.prototype._deref = function(queued) {
  this._refCount--;

  // Jobs that have reached the queue will always make it to the head
  if (queued) {
    this._running = null;
    this._state = null;
    this._cycle();
  }

  if (this._refCount === 0) {
    this.emit('drain');
  }
};

/**
 * If no job is currently being processed, start the one at the head of the
 * queue.
 * @private
 */
Resource.prototype._cycle = function() {
  var self = this;

  if (!this._running && this._jobs.length > 0) {
    this._running = this._jobs.shift();

    this._serializer.buildObject(this, function(err, state) {
      self._state = err || state;
      self.emit('cache', err, state);
      self._running.start();
    });
  }
};

/**
 * Called by jobs waiting to check their options. Only one job may be checking
 * its options at any given time.
 * @param {Function} callback A callback fired when job may check its options.
 */
Resource.prototype._waitToCheck = function(callback) {
  var self = this;

  function afterCheck() {
    self._waiters.shift();
    if (self._waiters.length > 0) {
      self._waiters[0](afterCheck);
    }
  }

  this._waiters.push(callback);
  if (this._waiters.length === 1) {
    this._waiters[0](afterCheck);
  }
};

/**
 * A resource that is backed by a directory.
 * @param {String} name The name of this resource's directory.
 * @constructor
 */
function DirectoryResource(name) {
  Resource.call(this, name);
  this.parentDir = this.getParentDir();
}

sys.inherits(DirectoryResource, Resource);

/**
 * Get parent directory of resources of this type. This must be set by
 * DirectoryResource implementations.
 * @returns {String} The path to the directory containing resource sof this
 *     type.
 */
DirectoryResource.prototype.getParentDir = function() { return undefined; };

/**
 * @inheritdoc
 */
DirectoryResource.prototype.list = function(callback) {
  fs.readdir(this.getParentDir(), callback);
};

/**
 * Get the path to the root of this resource.
 * @returns {String} Path to the root of this resource.
 */
DirectoryResource.prototype.getRoot = function() {
  return path.join(this.parentDir, this.name);
};

/**
 * Determine whether this resource exists by checking for its root directory.
 * @param {Function} callback A callback fired with (exists).
 */
DirectoryResource.prototype.exists = function(callback) {
  fs.stat(this.getRoot(), function(err, stats) {
    callback(!err && stats.isDirectory());
  });
};

/**
 * A ResourceManager manages access to resources in order to enforce locking,
 * queueing, etc. When a request to access a resource is received, the manager
 * will automatically construct resource objects as necessary, then deconstruct
 * them when they are no longer in use. The resource objects only provide
 * mechanisms for accessing the data they represent, and shouldn't try to
 * maintain resource state in memory.
 * @constructor
 */
function ResourceManager() {
  this.resourceType = undefined;
  this.resources = {};
}

/**
 * Retrieve, if necessary by instantiating, a resource with the specified name.
 * When a resource is instantiated it "job count" will be 0, but it won't be
 * removed until something causes it to emit a 'drain'. Thereore, if you are
 * going to call this you should make sure to kick off something that will
 * increment and later decrement the job counter.
 * @param {String} name The name of this job.
 */
ResourceManager.prototype._getOrCreate = function(name) {
  var self = this;

  // If necessary, instantiate the resource
  if (!this.resources[name]) {
    this.resources[name] = new this.resourceType(name);

    this.resources[name].on('drain', function() {
      delete self.resources[name];
    });
  }

  return this.resources[name];
};

/**
 * Execute the given job against the resource identified by the specified name.
 * @param {Object} job The job to execute.
 */
ResourceManager.prototype.runJob = function(job) {
  var self = this;
  var name = job.resourceName;

  job.enqueueFor(this._getOrCreate(name));
};

/**
 * Retrieve the serialized state of an instance.
 * @param {String} name The name of the resource to retreieve.
 * @param {Function} callback A callback fired with (err, state).
 */
ResourceManager.prototype.get = function(name, callback) {
  var self = this;
  var resource = this._getOrCreate(name);
  resource.getState(callback);
};

/**
 * Retrieve a "live" resource object. The resource will not be locked, and
 * jobs may be executing on it, so basically be very careful what you do with
 * it. You also need to call deref() on the resource you when you are done.
 * @param {String} name The name of the resource to retrieve.
 * @param {Function} callback A callback fired with (err, resource).
 */
ResourceManager.prototype.getLive = function(name, callback) {
  var self = this;
  var resource = this._getOrCreate(name);
  resource.alive(function(err, afterCheck) {
    if (err) {
      callback(err);
    } else {
      callback(null, resource);
      afterCheck();
    }
  });
};

/**
 * Get a serialized copy of each resource.
 * @param {Function} callback A callback fired with (err, states).
 */
ResourceManager.prototype.list = function(callback) {
  var self = this;

  function getNames(callback) {
    self.resourceType.prototype.list(callback);
  }

  function mapToStates(names, callback) {
    var resources = names.map(function(name) {
      return self._getOrCreate(name);
    });

    async.map(resources, function(resource, callback) {
      resource.getState(callback);
    },
    function(err, requests) {
      if (!err) {
        requests.sort(function(a, b) {
          return (a.name > b.name);
        });
      }
      callback(err, requests);
    });
  }

  async.waterfall([getNames, mapToStates], callback);
};

/**
 * Resource managers may optionally override this to do asynchronous
 * initialization tasks.
 * @param {Function} callback A callback fired with (err).
 */
ResourceManager.prototype.init = function(callback) {
  callback();
};

exports.ResourceManager = ResourceManager;
exports.DirectoryResource = DirectoryResource;
<|endoftext|>


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

var express = require('express');
var api = require('lib/db-api');
var utils = require('lib/utils');
var accepts = require('lib/accepts');
var restrict = utils.restrict;
var staff = utils.staff;
var pluck = utils.pluck;
var expose = utils.expose;
var log = require('debug')('democracyos:law');

var app = module.exports = express();

/**
 * Limit request to json format only
 */

app.use(accepts('application/json'));

app.get('/law/all', function (req, res) {
  log('Request /law/all');

  api.law.all(function(err, lawDocs) {
    if (err) return _handleError(err, req, res);

    // filter only public documents
    lawDocs = lawDocs.filter(function(law) {
      return law.public || (req.user && req.user.staff)
    });

    // add voted flag to returning documents
    lawDocs.forEach(function(law) {
      if (!req.user) return law.voted = false;
      law.voted = law.votedBy(req.user);
    });

    log('Serving laws %j', pluck(lawDocs, "id"));

    var keys = [
      'id lawId mediaTitle status open closed public draft deleted',
      'tag participants voted createdAt updatedAt closingAt',
      'publishedAt deletedAt votable clauseTruncationText links author authorUrl'
    ].join(' ');

    res.json(lawDocs.map(expose(keys)));
  });
});

app.get('/law/:id', function (req, res) {
  log('Request GET /law/%s', req.params.id);

  api.law.get(req.params.id, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);
    if (!lawDoc) return res.send(404);

    log('Serving law %s', lawDoc.id);
    var keys = [
      'id lawId title mediaTitle summary clauses source status open closed public draft deleted',
      'satus state tag participants',
      'upvotes downvotes abstentions createdAt updatedAt closingAt',
      'publishedAt deletedAt votable clauseTruncationText links author authorUrl'
    ].join(' ');

    res.json(expose(keys)(lawDoc.toJSON()));
  });
});

app.get('/law/:id/summary-comments', function (req, res) {
  log('Request /law/%s/summary-comments', req.params.id);

  api.law.get(req.params.id, function (err, law) {
    if (err) return _handleError(err, req, res);

    var re = new RegExp(req.params.id + '\-\\d');
    api.comment.getFor({context: 'summary', reference: re}, null, function (err, comments) {
      if (err) return _handleError(err, req, res);

      log('Serving law %s summary\'s comments %j', req.params.id, pluck(comments, 'id'));

      var keys = [
        'id text createdAt editedAt context reference',
        'author.id author.fullName author.gravatar()',
        'flags upvotes downvotes votes replies.length'
      ].join(' ');

      res.json(comments.map(expose(keys)));
    });

  });
});

app.get('/law/:id/clause-comments', function (req, res) {
  log('Request /law/%s/clause-comments', req.params.id);

  api.law.get(req.params.id, function (err, law) {
    if (err) return _handleError(err, req, res);

    var clauses = law.clauses.map(function (clause) { return clause.id });

    api.comment.getFor({context: 'clause', reference: { $in: clauses } }, null, function (err, comments) {
      if (err) return _handleError(err, req, res);

      log('Serving law %s clauses\' comments %j', req.params.id, pluck(comments, 'id'));

      var keys = [
        'id text createdAt editedAt context reference',
        'author.id author.fullName author.gravatar() author.profilePictureUrl',
        'flags upvotes downvotes votes replies.length'
      ].join(' ');

      res.json(comments.map(expose(keys)));
    });

  });
});

app.get('/law/:id/comments', function (req, res) {
  log('Request /law/%s/comments', req.params.id);

  var sort = '';
  if (~['-score', '-createdAt', 'createdAt'].indexOf(req.query.sort)) {
    sort = req.query.sort;
  } else {
    sort = '-score';
  }

  var paging = {
    page: req.query.page || 0,
    limit: req.query.limit || 0,
    sort: sort,
    exclude_user: req.query.exclude_user || null
  };

  api.law.comments(req.params.id, paging, function (err, comments) {
    if (err) return _handleError(err, req, res);

    if (!req.query.count) {
      log('Serving law %s comments %j', req.params.id, pluck(comments, 'id'));

      var keys = [
        "id text createdAt editedAt context reference",
        "author.id author.fullName author.gravatar() author.profilePictureUrl",
        "flags upvotes downvotes votes replies.length"
      ].join(' ');

      res.json(comments.map(expose(keys)));
    } else {
      log('Serving law %s comments count: %d', req.params.id, comments.length);

      res.json(comments.length);
    }
  });
});

app.get('/law/:id/my-comments', restrict, function (req, res) {
  log('Request /law/%s/my-comments', req.params.id);

  api.law.citizenComments(req.params.id, req.user.id, function (err, comments) {
    if (err) return _handleError(err, req, res);

    log('Serving law %s comments %j for citizen %s', req.params.id, pluck(comments, 'id'), req.user.id);

    var keys = [
      "id text createdAt editedAt context reference",
      "author.id author.fullName author.gravatar() author.profilePictureUrl",
      "flags upvotes downvotes votes replies.length"
    ].join(' ');

    res.json(comments.map(expose(keys)));
  });
});

app.post('/law/:id/comment', restrict, function (req, res) {
  log('Request /law/%s/comment %j', req.params.id, req.body.comment);

  var comment = req.body.comment;
  // This must be uncommented on production!
  comment.author = req.user;

  api.law.comment(comment, function (err, commentDoc) {
    if (err) return _handleError(err, req, res);

    var keys = [
      'id text',
      'author.id author.fullName author.gravatar() author.profilePictureUrl',
      'upvotes downvotes flags',
      'createdAt replies context reference'
    ].join(' ');

    res.json(200, expose(keys)(commentDoc));
  });
});

app.post('/law/:id/vote', function (req, res) {
  log('Request /law/%s/vote', req.param('id'));

  api.law
  .vote(
    req.param('id'),
    req.user,
    req.param('value'),
    function (err, comments) {
      if (err) return _handleError(err, req, res);

      log('Serving 200 OK response');
      res.send(200);
    }
  );
});

app.post('/law/create', restrict, staff, function (req, res, next) {
  log('Request /law/create %j', req.body.law);

  api.law.create(req.body, function (err, lawDoc) {
    if (err) return next(err);
    var keys = [
      'id lawId title mediaTitle summary clauses source state',
      'status open closed public draft deleted tag participants',
      'upvotes downvotes abstentions createdAt updatedAt closingAt',
      'publishedAt deletedAt votable clauseTruncationText links author authorUrl'
    ].join(' ');
    res.json(expose(keys)(lawDoc));
  });
});

app.post('/law/:id', restrict, staff, function (req, res) {
  log('Request POST /law/%s', req.params.id);

  api.law.update(req.params.id, req.body, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);

    log('Serving law %s', lawDoc.id);
    var keys = [
      'id lawId title mediaTitle summary clauses source state',
      'status open closed public draft deleted tag participants',
      'upvotes downvotes abstentions createdAt updatedAt closingAt',
      'publishedAt deletedAt votable clauseTruncationText links author authorUrl'
    ].join(' ');

    res.json(expose(keys)(lawDoc.toJSON()));
  });
});

app.post('/law/:id/clause', restrict, staff, function (req, res) {
  log('Request POST /law/%s/clause', req.params.id);

  var clause = req.body.clause;
  api.law.get(req.params.id, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);

    var clauseDoc = clause && clause.id
      ? lawDoc.clauses.id(clause.id)
      : lawDoc.clauses.create();

    clauseDoc.update(clause);
    if (clauseDoc.isNew) lawDoc.clauses.push(clauseDoc);

    lawDoc.save(function (err, saved) {
      if (err) return _handleError(err, req, res);

      res.json(200, expose('id order clauseName text centered')(clauseDoc));
    });
  });
});

app.del('/law/:id/clause', restrict, staff, function (req, res) {
  log('Request DELETE /law/%s/clause', req.params.id);

  var clause = req.body.clause;
  api.law.get(req.params.id, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);

    lawDoc.clauses.remove(clause);
    log('removed clause %s from law %s', clause, lawDoc.id);

    lawDoc.save(function (err, saved) {
      if (err) return _handleError(err, req, res);

      res.json(200);
    });
  });
});

app.post('/law/:id/link', restrict, staff, function (req, res) {
  log('Request POST /law/%s/link', req.params.id);

  var link = req.body.link;
  api.law.get(req.params.id, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);

    var linkDoc = link && link.id
      ? lawDoc.links.id(link.id)
      : lawDoc.links.create();

    linkDoc.update(link);
    if (linkDoc.isNew) lawDoc.links.push(linkDoc);

    lawDoc.save(function (err, saved) {
      if (err) return _handleError(err, req, res);

      res.json(200, expose('id text url')(linkDoc));
    });
  });
});

app.del('/law/:id/link', restrict, staff, function (req, res) {
  log('Request DELETE /law/%s/link', req.params.id);

  var link = req.body.link;
  api.law.get(req.params.id, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);

    lawDoc.links.remove(link);
    log('removed link %s from law %s', link, lawDoc.id);

    lawDoc.save(function (err, saved) {
      if (err) return _handleError(err, req, res);

      res.json(200);
    });
  });
});

app.post('/law/:id/publish', restrict, staff, function (req, res) {
  log('Request POST /law/%s/publish', req.params.id);

  api.law.get(req.params.id, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);

    lawDoc.publishedAt = new Date;
    lawDoc.save(function (err, saved) {
      if (err) return _handleError(err, req, res);
      log('publish law %s at %s', lawDoc.id, lawDoc.publishedAt);
      res.json(200);
    });
  });
});

app.post('/law/:id/unpublish', restrict, staff, function (req, res) {
  log('Request POST /law/%s/unpublish', req.params.id);

  api.law.get(req.params.id, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);

    lawDoc.publishedAt = null;
    lawDoc.save(function (err, saved) {
      if (err) return _handleError(err, req, res);
      log('unpublished law %s', lawDoc.id);
      res.json(200);
    });
  });
});

app.post('/law/:id/delete', restrict, staff, function (req, res) {
  log('Request POST /law/%s/delete', req.params.id);

  api.law.get(req.params.id, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);

    lawDoc.deletedAt = new Date;
    lawDoc.save(function (err, saved) {
      if (err) return _handleError(err, req, res);
      log('deleted law %s', lawDoc.id);
      res.json(200);
    });
  });
});

function _handleError (err, req, res) {
  log("Error found: %s", err);
  var error = err;
  if (err.errors && err.errors.text) error = err.errors.text;
  if (error.type) error = error.type;

  res.json(400, { error: error });
}<commit_msg>[law-api] - Send notification on published law to users who have subscribed to the law's tag<commit_after>/**
 * Module dependencies.
 */

var express = require('express');
var api = require('lib/db-api');
var utils = require('lib/utils');
var accepts = require('lib/accepts');
var restrict = utils.restrict;
var staff = utils.staff;
var pluck = utils.pluck;
var expose = utils.expose;
var log = require('debug')('democracyos:law');
var config = require('lib/config');
var url = require('url');
var notifier = require('notifier-client')(config.notifications);

var app = module.exports = express();

/**
 * Limit request to json format only
 */

app.use(accepts('application/json'));

app.get('/law/all', function (req, res) {
  log('Request /law/all');

  api.law.all(function(err, lawDocs) {
    if (err) return _handleError(err, req, res);

    // filter only public documents
    lawDocs = lawDocs.filter(function(law) {
      return law.public || (req.user && req.user.staff)
    });

    // add voted flag to returning documents
    lawDocs.forEach(function(law) {
      if (!req.user) return law.voted = false;
      law.voted = law.votedBy(req.user);
    });

    log('Serving laws %j', pluck(lawDocs, "id"));

    var keys = [
      'id lawId mediaTitle status open closed public draft deleted',
      'tag participants voted createdAt updatedAt closingAt',
      'publishedAt deletedAt votable clauseTruncationText links author authorUrl'
    ].join(' ');

    res.json(lawDocs.map(expose(keys)));
  });
});

app.get('/law/:id', function (req, res) {
  log('Request GET /law/%s', req.params.id);

  api.law.get(req.params.id, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);
    if (!lawDoc) return res.send(404);

    log('Serving law %s', lawDoc.id);
    var keys = [
      'id lawId title mediaTitle summary clauses source status open closed public draft deleted',
      'satus state tag participants',
      'upvotes downvotes abstentions createdAt updatedAt closingAt',
      'publishedAt deletedAt votable clauseTruncationText links author authorUrl'
    ].join(' ');

    res.json(expose(keys)(lawDoc.toJSON()));
  });
});

app.get('/law/:id/summary-comments', function (req, res) {
  log('Request /law/%s/summary-comments', req.params.id);

  api.law.get(req.params.id, function (err, law) {
    if (err) return _handleError(err, req, res);

    var re = new RegExp(req.params.id + '\-\\d');
    api.comment.getFor({context: 'summary', reference: re}, null, function (err, comments) {
      if (err) return _handleError(err, req, res);

      log('Serving law %s summary\'s comments %j', req.params.id, pluck(comments, 'id'));

      var keys = [
        'id text createdAt editedAt context reference',
        'author.id author.fullName author.gravatar()',
        'flags upvotes downvotes votes replies.length'
      ].join(' ');

      res.json(comments.map(expose(keys)));
    });

  });
});

app.get('/law/:id/clause-comments', function (req, res) {
  log('Request /law/%s/clause-comments', req.params.id);

  api.law.get(req.params.id, function (err, law) {
    if (err) return _handleError(err, req, res);

    var clauses = law.clauses.map(function (clause) { return clause.id });

    api.comment.getFor({context: 'clause', reference: { $in: clauses } }, null, function (err, comments) {
      if (err) return _handleError(err, req, res);

      log('Serving law %s clauses\' comments %j', req.params.id, pluck(comments, 'id'));

      var keys = [
        'id text createdAt editedAt context reference',
        'author.id author.fullName author.gravatar() author.profilePictureUrl',
        'flags upvotes downvotes votes replies.length'
      ].join(' ');

      res.json(comments.map(expose(keys)));
    });

  });
});

app.get('/law/:id/comments', function (req, res) {
  log('Request /law/%s/comments', req.params.id);

  var sort = '';
  if (~['-score', '-createdAt', 'createdAt'].indexOf(req.query.sort)) {
    sort = req.query.sort;
  } else {
    sort = '-score';
  }

  var paging = {
    page: req.query.page || 0,
    limit: req.query.limit || 0,
    sort: sort,
    exclude_user: req.query.exclude_user || null
  };

  api.law.comments(req.params.id, paging, function (err, comments) {
    if (err) return _handleError(err, req, res);

    if (!req.query.count) {
      log('Serving law %s comments %j', req.params.id, pluck(comments, 'id'));

      var keys = [
        "id text createdAt editedAt context reference",
        "author.id author.fullName author.gravatar() author.profilePictureUrl",
        "flags upvotes downvotes votes replies.length"
      ].join(' ');

      res.json(comments.map(expose(keys)));
    } else {
      log('Serving law %s comments count: %d', req.params.id, comments.length);

      res.json(comments.length);
    }
  });
});

app.get('/law/:id/my-comments', restrict, function (req, res) {
  log('Request /law/%s/my-comments', req.params.id);

  api.law.citizenComments(req.params.id, req.user.id, function (err, comments) {
    if (err) return _handleError(err, req, res);

    log('Serving law %s comments %j for citizen %s', req.params.id, pluck(comments, 'id'), req.user.id);

    var keys = [
      "id text createdAt editedAt context reference",
      "author.id author.fullName author.gravatar() author.profilePictureUrl",
      "flags upvotes downvotes votes replies.length"
    ].join(' ');

    res.json(comments.map(expose(keys)));
  });
});

app.post('/law/:id/comment', restrict, function (req, res) {
  log('Request /law/%s/comment %j', req.params.id, req.body.comment);

  var comment = req.body.comment;
  // This must be uncommented on production!
  comment.author = req.user;

  api.law.comment(comment, function (err, commentDoc) {
    if (err) return _handleError(err, req, res);

    var keys = [
      'id text',
      'author.id author.fullName author.gravatar() author.profilePictureUrl',
      'upvotes downvotes flags',
      'createdAt replies context reference'
    ].join(' ');

    res.json(200, expose(keys)(commentDoc));
  });
});

app.post('/law/:id/vote', function (req, res) {
  log('Request /law/%s/vote', req.param('id'));

  api.law
  .vote(
    req.param('id'),
    req.user,
    req.param('value'),
    function (err, comments) {
      if (err) return _handleError(err, req, res);

      log('Serving 200 OK response');
      res.send(200);
    }
  );
});

app.post('/law/create', restrict, staff, function (req, res, next) {
  log('Request /law/create %j', req.body.law);

  api.law.create(req.body, function (err, lawDoc) {
    if (err) return next(err);
    var keys = [
      'id lawId title mediaTitle summary clauses source state',
      'status open closed public draft deleted tag participants',
      'upvotes downvotes abstentions createdAt updatedAt closingAt',
      'publishedAt deletedAt votable clauseTruncationText links author authorUrl'
    ].join(' ');
    res.json(expose(keys)(lawDoc));
  });
});

app.post('/law/:id', restrict, staff, function (req, res) {
  log('Request POST /law/%s', req.params.id);

  api.law.update(req.params.id, req.body, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);

    log('Serving law %s', lawDoc.id);
    var keys = [
      'id lawId title mediaTitle summary clauses source state',
      'status open closed public draft deleted tag participants',
      'upvotes downvotes abstentions createdAt updatedAt closingAt',
      'publishedAt deletedAt votable clauseTruncationText links author authorUrl'
    ].join(' ');

    res.json(expose(keys)(lawDoc.toJSON()));
  });
});

app.post('/law/:id/clause', restrict, staff, function (req, res) {
  log('Request POST /law/%s/clause', req.params.id);

  var clause = req.body.clause;
  api.law.get(req.params.id, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);

    var clauseDoc = clause && clause.id
      ? lawDoc.clauses.id(clause.id)
      : lawDoc.clauses.create();

    clauseDoc.update(clause);
    if (clauseDoc.isNew) lawDoc.clauses.push(clauseDoc);

    lawDoc.save(function (err, saved) {
      if (err) return _handleError(err, req, res);

      res.json(200, expose('id order clauseName text centered')(clauseDoc));
    });
  });
});

app.del('/law/:id/clause', restrict, staff, function (req, res) {
  log('Request DELETE /law/%s/clause', req.params.id);

  var clause = req.body.clause;
  api.law.get(req.params.id, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);

    lawDoc.clauses.remove(clause);
    log('removed clause %s from law %s', clause, lawDoc.id);

    lawDoc.save(function (err, saved) {
      if (err) return _handleError(err, req, res);

      res.json(200);
    });
  });
});

app.post('/law/:id/link', restrict, staff, function (req, res) {
  log('Request POST /law/%s/link', req.params.id);

  var link = req.body.link;
  api.law.get(req.params.id, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);

    var linkDoc = link && link.id
      ? lawDoc.links.id(link.id)
      : lawDoc.links.create();

    linkDoc.update(link);
    if (linkDoc.isNew) lawDoc.links.push(linkDoc);

    lawDoc.save(function (err, saved) {
      if (err) return _handleError(err, req, res);

      res.json(200, expose('id text url')(linkDoc));
    });
  });
});

app.del('/law/:id/link', restrict, staff, function (req, res) {
  log('Request DELETE /law/%s/link', req.params.id);

  var link = req.body.link;
  api.law.get(req.params.id, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);

    lawDoc.links.remove(link);
    log('removed link %s from law %s', link, lawDoc.id);

    lawDoc.save(function (err, saved) {
      if (err) return _handleError(err, req, res);

      res.json(200);
    });
  });
});

app.post('/law/:id/publish', restrict, staff, function (req, res) {
  log('Request POST /law/%s/publish', req.params.id);

  api.law.get(req.params.id, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);

    lawDoc.publishedAt = new Date;
    lawDoc.save(function (err, saved) {
      if (err) return _handleError(err, req, res);
      log('publish law %s at %s', lawDoc.id, lawDoc.publishedAt);

      if (notifier.enabled()) {

          var lawUrl = url.format({
              protocol: config('protocol')
            , hostname: config('host')
            , port: config('publicPort')
            , pathname: '/law/' + lawDoc.id
          });

          notifier.notify('law-published')
            .withData( { law: lawDoc, url: lawUrl } )
            .send(function (err, data) {
              if (err) {
                log('Error when sending notification for event %s', 'law-published');
                return _handleError(new Error('Error when sending notification for event %s', 'law-published'), req, res);
              }

              log('Law %s published', lawDoc.id);
              return res.json(200);
            })
      } else {
        if (err) return _handleError(err, req, res);
        return res.json(200);
      }
    });
  });
});

app.post('/law/:id/unpublish', restrict, staff, function (req, res) {
  log('Request POST /law/%s/unpublish', req.params.id);

  api.law.get(req.params.id, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);

    lawDoc.publishedAt = null;
    lawDoc.save(function (err, saved) {
      if (err) return _handleError(err, req, res);
      log('unpublished law %s', lawDoc.id);
      res.json(200);
    });
  });
});

app.post('/law/:id/delete', restrict, staff, function (req, res) {
  log('Request POST /law/%s/delete', req.params.id);

  api.law.get(req.params.id, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);

    lawDoc.deletedAt = new Date;
    lawDoc.save(function (err, saved) {
      if (err) return _handleError(err, req, res);
      log('deleted law %s', lawDoc.id);
      res.json(200);
    });
  });
});

function _handleError (err, req, res) {
  log("Error found: %s", err);
  var error = err;
  if (err.errors && err.errors.text) error = err.errors.text;
  if (error.type) error = error.type;

  res.json(400, { error: error });
}<|endoftext|>


<commit_before>'use babel';

const cp = require('child-process-es6-promise');
const regexp = /(.*):(\d+):\s+\[(.)\]\s+(.*)/;
const React = require('react');
const fs = require('fs');

class LinterPython extends React.Component {

    constructor() {
        super();

        this.lint = this.lint.bind(this);
        this.initialPlugin = this.initialPlugin.bind(this);
    }

    initialPlugin() {
        let linters = [];

        this.executablePath = this.readConfigValue('executablePath');
        this.pylamaArgs = [];

        let enablePep8 = this.readConfigValue('enablePep8');
        if (enablePep8) {
            linters.push('pep8');
        }

        let enablePep257 = this.readConfigValue('enablePep257');
        if (enablePep257) {
            linters.push('pep257');
        }

        let enableMcCabe = this.readConfigValue('enableMcCabe');
        if (enableMcCabe) {
            linters.push('mccabe');
        }

        let enablePyflakes = this.readConfigValue('enablePyflakes');
        if (enablePyflakes) {
            linters.push('pyflakes');
        }

        let enablePylint = this.readConfigValue('enablePylint');
        if (enablePylint) {
            linters.push('pylint');
        }

        if (linters.length > 0) {
            this.pylamaArgs.push('-l');
            this.pylamaArgs.push(linters.join());
        }

        let ignoreCodes = this.readConfigValue('ignoreCodes');
        if (ignoreCodes) {
            this.pylamaArgs.push('-i');
            this.pylamaArgs.push(ignoreCodes);
        }
    }

    readConfigValue(value) {
        try {
            return atom.config.get('linter-python.' + value);
        }
        catch(err) {
            console.log(err);
            return '';
        }
    }

    parseLines(data) {
        let results = [];
        let lines = data.split('\n');
        for (let line of lines) {
            results.push(line.match(regexp));
        }
        return results;
    }

    sortBy(key, a, b) {
        if (a[key] == 'Error' && b[key] == 'Warning') {
            return -1;
        }
        if (a[key] == 'Warning' && b[key] == 'Error') {
            return 1;
        }
        return 0;
    }

    buildMessage(textEditor, result) {
        let lineNumber = textEditor.buffer.lines[result[2]];
        let colEnd = lineNumber.length;
        let resultType = 'Warning';
        if (result[3] == 'E' || result[3] == 'F') {
            resultType = 'Error';
        }
        return {
            type: resultType,
            text: result[4],
            filePath: textEditor.getPath(),
            range: [
                [result[2] - 1, 0],
                [result[2] - 1, colEnd]
            ]
        };
    }

    isExecutablePathExists(path) {
        try {
            fs.accessSync(path, fs.R_OK | fs.X_OK);
            return true;
        }
        catch(err) {
            return false;
        }
    }

    lint(textEditor) {
        return new Promise ((resolve, reject) => {
            let messages = [];
            let buff = [];
            let cmd = this.executablePath;
            let args = this.pylamaArgs.concat(['-f', 'pylint', textEditor.getPath()]);
            if (!this.isExecutablePathExists(cmd)) {
                atom.notifications.addError("Provided path doesn't exist. Please fix pylama path.");
                resolve([]);
            }
            cp.spawn(cmd, args, {})
                .then((result) => {
                    results = this.parseLines(result.stdout);

                    for (result of results) {
                        if (result) {
                            message = this.buildMessage(textEditor, result);
                            messages.push(message);
                        }
                    }
                    let sorted_messages = messages.sort((a, b) => {
                        return this.sortBy('type', a, b);
                    });
                    resolve(sorted_messages);
                })
                .catch((error) => {
                    console.log(error);
                    resolve([]);
                });
        });
    }
}

module.exports = LinterPython;
<commit_msg>Update pylama results only on save (close #4).<commit_after>'use babel';

const cp = require('child-process-es6-promise');
const regexp = /(.*):(\d+):\s+\[(.)\]\s+(.*)/;
const React = require('react');
const fs = require('fs');

class LinterPython extends React.Component {

    constructor() {
        super();

        this.lastMessages = [];
        this.lint = this.lint.bind(this);
        this.initialPlugin = this.initialPlugin.bind(this);
    }

    initialPlugin() {
        let linters = [];

        this.executablePath = this.readConfigValue('executablePath');
        this.pylamaArgs = [];

        let enablePep8 = this.readConfigValue('enablePep8');
        if (enablePep8) {
            linters.push('pep8');
        }

        let enablePep257 = this.readConfigValue('enablePep257');
        if (enablePep257) {
            linters.push('pep257');
        }

        let enableMcCabe = this.readConfigValue('enableMcCabe');
        if (enableMcCabe) {
            linters.push('mccabe');
        }

        let enablePyflakes = this.readConfigValue('enablePyflakes');
        if (enablePyflakes) {
            linters.push('pyflakes');
        }

        let enablePylint = this.readConfigValue('enablePylint');
        if (enablePylint) {
            linters.push('pylint');
        }

        if (linters.length > 0) {
            this.pylamaArgs.push('-l');
            this.pylamaArgs.push(linters.join());
        }

        let ignoreCodes = this.readConfigValue('ignoreCodes');
        if (ignoreCodes) {
            this.pylamaArgs.push('-i');
            this.pylamaArgs.push(ignoreCodes);
        }
    }

    readConfigValue(value) {
        try {
            return atom.config.get('linter-python.' + value);
        }
        catch(err) {
            console.log(err);
            return '';
        }
    }

    parseLines(data) {
        let results = [];
        let lines = data.split('\n');
        for (let line of lines) {
            results.push(line.match(regexp));
        }
        return results;
    }

    sortBy(key, a, b) {
        if (a[key] == 'Error' && b[key] == 'Warning') {
            return -1;
        }
        if (a[key] == 'Warning' && b[key] == 'Error') {
            return 1;
        }
        return 0;
    }

    buildMessage(textEditor, result) {
        let lineNumber = textEditor.buffer.lines[result[2]];
        let colEnd = lineNumber.length;
        let resultType = 'Warning';
        if (result[3] == 'E' || result[3] == 'F') {
            resultType = 'Error';
        }
        return {
            type: resultType,
            text: result[4],
            filePath: textEditor.getPath(),
            range: [
                [result[2] - 1, 0],
                [result[2] - 1, colEnd]
            ]
        };
    }

    isExecutablePathExists(path) {
        try {
            fs.accessSync(path, fs.R_OK | fs.X_OK);
            return true;
        }
        catch(err) {
            return false;
        }
    }

    lint(textEditor) {
        return new Promise ((resolve, reject) => {
            let messages = [];
            let buff = [];
            let cmd = this.executablePath;
            let args = this.pylamaArgs.concat(['-f', 'pylint', textEditor.getPath()]);
            if (textEditor.isModified()) {
                resolve(this.lastMessages);
            }
            if (!this.isExecutablePathExists(cmd)) {
                atom.notifications.addError("Provided path doesn't exist. Please fix pylama path.");
                resolve([]);
            }
            cp.spawn(cmd, args, {})
                .then((result) => {
                    results = this.parseLines(result.stdout);

                    for (result of results) {
                        if (result) {
                            message = this.buildMessage(textEditor, result);
                            messages.push(message);
                        }
                    }
                    this.lastMessages = messages.sort((a, b) => {
                        return this.sortBy('type', a, b);
                    });
                    resolve(this.lastMessages);
                })
                .catch((error) => {
                    atom.notifications.addError("Execution finished with error: " + error);
                    console.log(error);
                    resolve([]);
                });
        });
    }
}

module.exports = LinterPython;
<|endoftext|>


<commit_before>'use strict';

var utils = require('./utils');
const NA = 'N/A';

var auditRequest = function(req, options){
    var shouldAudit = utils.shouldAuditURL(options.excludeURLs, req);
    if (shouldAudit){
        var request;
        if (options.request.audit){
            request = getRequestAudit(req, options);
        }

        var auditObject = {
            request: request
        };

        // Add additional audit fields
        if (req && req.additionalAudit){
            auditObject = Object.assign(auditObject, req.additionalAudit);
        }

        options.logger.info(auditObject);
    }
};

var auditResponse = function(req, res, options){
    var request;
    var response;

    var shouldAudit = utils.shouldAuditURL(options.excludeURLs, req);
    if (shouldAudit){
        if (options.request.audit){
            request = getRequestAudit(req, options);
        }

        if (options.response.audit){
            response = getResponseAudit(req, res, options);
        }

        var auditObject = {
            response: response,
            request: request
        };

        // Add additional audit fields
        if (req && req.additionalAudit){
            auditObject = Object.assign(auditObject, req.additionalAudit);
        }

        options.logger.info(auditObject);
    }
};

function getRequestAudit(req, options){
    var headers = req && req.headers ? req.headers : NA;
    var requestURL = utils.getUrl(req);
    var queryParams = req && req.query !== {} ? req.query : NA;
    var method = req && req.method ? req.method : NA;

    var requestBody;

    // Mask Body:
    if (req && req.body){
        var requestBody = req.body;
        requestBody = getMaskedBody(requestBody, options.request.maskBody);
        queryParams = getMaskedQuery(queryParams, options.request.maskQuery)
    } else {
        requestBody = NA;
    }

    // Exclude Headers:
    if (options.request.excludeHeaders && Array.isArray(options.request.excludeHeaders) && options.request.excludeHeaders.length > 0){
        options.request.excludeHeaders.forEach(function(header) {
            delete headers[header];
        }, this);
    }

    var auditObject = {
        method: method,
        url: requestURL,
        headers: headers,
        query: queryParams,
        body: requestBody,
    };

    return auditObject;
}

function getResponseAudit(req, res, options){
    var elapsed = req ? new Date() - req.startTime : 0;
    var statusCode = res && res.statusCode ? res.statusCode : NA;

    // Mask Body:
    if (res && res._body){
        var responseBody = res._body;
        try {
            responseBody = JSON.parse(responseBody);
        } catch (err){
            if (statusCode != 404){
                options.logger.error('Error parsing response json: ' + responseBody);
            }
            responseBody = NA;
        }
        responseBody = getMaskedBody(responseBody, options.response.maskBody);
    } else {
        responseBody = NA;
    }

    var auditObject = {
        status_code: statusCode,
        elapsed: elapsed,
        body: responseBody
    };

    return auditObject;
}

function getMaskedBody(body, fieldsToMask){
    // Mask Body:
    var result = body;
    if (body){
        var maskedBody = utils.maskJson(body, fieldsToMask);
        result = JSON.stringify(maskedBody);

    } else {
        result = NA;
    }
    return result;
}

function getMaskedQuery(query, fieldsToMask) {
    if (query) {
        return utils.maskJson(query, fieldsToMask);
    } else {
        return NA;
    }
}

module.exports = {
    auditRequest: auditRequest,
    auditResponse: auditResponse
};<commit_msg>Added option to run a setup function before building audit object<commit_after>'use strict';

var utils = require('./utils');
const NA = 'N/A';

var auditRequest = function(req, options){
    var shouldAudit = utils.shouldAuditURL(options.excludeURLs, req);
    if (shouldAudit){
        var request;

        if (options.setupFunc){
            options.setupFunc(req, res);
        }

        if (options.request.audit){
            request = getRequestAudit(req, options);
        }

        var auditObject = {
            request: request
        };

        // Add additional audit fields
        if (req && req.additionalAudit){
            auditObject = Object.assign(auditObject, req.additionalAudit);
        }

        options.logger.info(auditObject);
    }
};

var auditResponse = function(req, res, options){
    var request;
    var response;

    var shouldAudit = utils.shouldAuditURL(options.excludeURLs, req);
    if (shouldAudit){
        if (options.setupFunc){
            options.setupFunc(req, res);
        }

        if (options.request.audit){
            request = getRequestAudit(req, options);
        }

        if (options.response.audit){
            response = getResponseAudit(req, res, options);
        }

        var auditObject = {
            response: response,
            request: request
        };

        // Add additional audit fields
        if (req && req.additionalAudit){
            auditObject = Object.assign(auditObject, req.additionalAudit);
        }

        options.logger.info(auditObject);
    }
};

function getRequestAudit(req, options){
    var headers = req && req.headers ? req.headers : NA;
    var requestURL = utils.getUrl(req);
    var queryParams = req && req.query !== {} ? req.query : NA;
    var method = req && req.method ? req.method : NA;

    var requestBody;

    // Mask Body:
    if (req && req.body){
        var requestBody = req.body;
        requestBody = getMaskedBody(requestBody, options.request.maskBody);
        queryParams = getMaskedQuery(queryParams, options.request.maskQuery)
    } else {
        requestBody = NA;
    }

    // Exclude Headers:
    if (options.request.excludeHeaders && Array.isArray(options.request.excludeHeaders) && options.request.excludeHeaders.length > 0){
        options.request.excludeHeaders.forEach(function(header) {
            delete headers[header];
        }, this);
    }

    var auditObject = {
        method: method,
        url: requestURL,
        headers: headers,
        query: queryParams,
        body: requestBody,
    };

    return auditObject;
}

function getResponseAudit(req, res, options){
    var elapsed = req ? new Date() - req.startTime : 0;
    var statusCode = res && res.statusCode ? res.statusCode : NA;

    // Mask Body:
    if (res && res._body){
        var responseBody = res._body;
        try {
            responseBody = JSON.parse(responseBody);
        } catch (err){
            if (statusCode != 404){
                options.logger.error('Error parsing response json: ' + responseBody);
            }
            responseBody = NA;
        }
        responseBody = getMaskedBody(responseBody, options.response.maskBody);
    } else {
        responseBody = NA;
    }

    var auditObject = {
        status_code: statusCode,
        elapsed: elapsed,
        body: responseBody
    };

    return auditObject;
}

function getMaskedBody(body, fieldsToMask){
    // Mask Body:
    var result = body;
    if (body){
        var maskedBody = utils.maskJson(body, fieldsToMask);
        result = JSON.stringify(maskedBody);

    } else {
        result = NA;
    }
    return result;
}

function getMaskedQuery(query, fieldsToMask) {
    if (query) {
        return utils.maskJson(query, fieldsToMask);
    } else {
        return NA;
    }
}

module.exports = {
    auditRequest: auditRequest,
    auditResponse: auditResponse
};<|endoftext|>


<commit_before>var util = require('util');
var EventEmitter = require('events').EventEmitter;

exports.Logger = Logger;
exports.ConsoleLogger = ConsoleLogger;

/**
 * The logger.
 */
function Logger() {
}
util.inherits(Logger, EventEmitter)

Logger.prototype.log = function(category, message) {
  this.emit(category, category, message);
}

function ConsoleLogger(logger, options) {
  options = options || { colors: false };

  var f = options.facilities || ['warning', 'error', 'fatal', 'info', 'debug', 'custom'];

  for (var i = 0; i < f.length; ++i) {
    logger.on(f[i], function (lvl, msg) {
      ConsoleLogger.write(options, lvl, msg);
    });
  }
}

ConsoleLogger.colors = {
    info: '\033[00;33m%s\033[0m %s',
    custom: '\033[00;33m%s\033[0m %s',

    warning: '\033[00;31m%s\033[0m %s',
    error: '\033[00;31m%s\033[0m %s',
    fatal: '\033[00;31m%s\033[0m %s',

    debug: '\033[00;36m%s\033[0m %s',
}
ConsoleLogger.defaultTemplate = "%s: %s";

ConsoleLogger.write = function (options, level, msg) {
  var tpl = ConsoleLogger.defaultTemplate;

  // Override for color console.
  if (options.colors && ConsoleLogger.colors[level]) {
    tpl = ConsoleLogger.colors[level];
  }

  switch (level) {
    case "fatal":
    case "error":
    case "warning":
      console.error(tpl, level, msg);
      break;
    case "custom":
    case "info":
    case "debug":
    default:
      console.warn(tpl, level, msg);
  }
}
<commit_msg>Fixed colors on logging.<commit_after>var util = require('util');
var EventEmitter = require('events').EventEmitter;

exports.Logger = Logger;
exports.ConsoleLogger = ConsoleLogger;

/**
 * The logger.
 */
function Logger() {
}
util.inherits(Logger, EventEmitter)

Logger.prototype.log = function(category, message) {
  this.emit(category, category, message);
}

function ConsoleLogger(logger, options) {
  options = options || { colors: false };

  var f = options.facilities || ['warning', 'error', 'fatal', 'info', 'debug', 'access', 'custom'];

  for (var i = 0; i < f.length; ++i) {
    logger.on(f[i], function (lvl, msg) {
      ConsoleLogger.write(options, lvl, msg);
    });
  }
}

ConsoleLogger.colors = {
  access: '\033[00;32m%s\033[0m %s',

  info: '\033[00;33m%s\033[0m %s',
  custom: '\033[00;33m%s\033[0m %s',

  warning: '\033[00;31m%s\033[0m %s',
  error: '\033[00;31m%s\033[0m %s',
  fatal: '\033[00;31m%s\033[0m %s',

  debug: '\033[00;36m%s\033[0m %s',
}
ConsoleLogger.defaultTemplate = "%s: %s";

ConsoleLogger.write = function (options, level, msg) {
  var tpl = ConsoleLogger.defaultTemplate;

  // Override for color console.
  if (options.colors && ConsoleLogger.colors[level]) {
    tpl = ConsoleLogger.colors[level];
  }

  switch (level) {
    case "fatal":
    case "error":
    case "warning":
      console.error(tpl, level, msg);
      break;
    case "access":
    case "custom":
    case "info":
    case "debug":
    default:
      console.warn(tpl, level, msg);
  }
}
<|endoftext|>


<commit_before>'use strict';

var zmq = require('zmq')
var dbc = require('dbc')

var __hasProp = Object.prototype.hasOwnProperty

var status = {
   OK_200                     : 200,
   NO_CONTENT_204             : 204,
   BAD_REQUEST_400            : 400,
   UNAUTHORIZED_401           : 401,
   FORBIDDEN_402              : 402,
   NOT_FOUND_404              : 404,
   INTERNAL_SERVER_ERROR_500  : 500
}

var header_plain = { "access-control-allow-origin" : '*', 'Content-Type': 'text/plain; charset=utf-8'       }
var header_json  = { "access-control-allow-origin" : '*', 'Content-Type': 'application/json; charset=utf-8' }
var header_html  = { "access-control-allow-origin" : '*', 'Content-Type': 'text/html; charset=utf-8'        }

var statusMessages = {
   '100': 'Continue',
   '101': 'Switching Protocols',
   '200': 'OK',
   '201': 'Created',
   '202': 'Accepted',
   '203': 'Non-Authoritative Information',
   '204': 'No Content',
   '205': 'Reset Content',
   '206': 'Partial Content',
   '300': 'Multiple Choices',
   '301': 'Moved Permanently',
   '302': 'Found',
   '303': 'See Other',
   '304': 'Not Modified',
   '305': 'Use Proxy',
   '307': 'Temporary Redirect',
   '400': 'Bad Request',
   '401': 'Unauthorized',
   '402': 'Payment Required',
   '403': 'Forbidden',
   '404': 'Not Found',
   '405': 'Method Not Allowed',
   '406': 'Not Acceptable',
   '407': 'Proxy Authentication Required',
   '408': 'Request Timeout',
   '409': 'Conflict',
   '410': 'Gone',
   '411': 'Length Required',
   '412': 'Precondition Failed',
   '413': 'Request Entity Too Large',
   '414': 'Request-URI Too Large',
   '415': 'Unsupported Media Type',
   '416': 'Request Range Not Satisfiable',
   '417': 'Expectation Failed',
   '500': 'Internal Server Error',
   '501': 'Not Implemented',
   '502': 'Bad Gateway',
   '503': 'Service Unavailable',
   '504': 'Gateway Timeout',
   '505': 'HTTP Version Not Supported'
}

function split(str, chr, limit) {
   var parts = str.split  (chr)
   var ret   = parts.slice(0, limit - 1)

   ret.push(parts.slice(limit - 1).join(chr))
   return ret
}

function parseNetstring(ns) {

   var nsSplit = split(ns, ':', 2)
   var length  = parseInt  (nsSplit[0], 10)
   var payload = nsSplit[1]

   if (payload[length] !== ',') {
      throw 'Netstring did not end in ","'
   }

   return [ payload.slice(0, length), payload.slice(length + 1) ]
}

function getJSON(str){
   try{
      return JSON.parse(str)
   }
   catch(e) {
      return null
   }
}

function isValidJSON(str){
   if (0 === str.length){
      return true
   }
   try{
      JSON.parse(str)
      return true;
   }
   catch(e) {
      return false
   }
}

function parse(mongrelString) {
   var mongrelStringSplit = split(mongrelString, ' ', 4)

   var uuid       = mongrelStringSplit[0]
   var connId     = mongrelStringSplit[1]
   var path       = mongrelStringSplit[2]
   var payload    = mongrelStringSplit[3]

   var paySplit   = parseNetstring(payload)
   var headers    = getJSON       (paySplit[0])
   var body       = parseNetstring(paySplit[1])[0]

   var valid_json = isValidJSON(body)
   var json       = getJSON(body)

   return {
      headers    : headers,
      uuid       : uuid,
      connId     : connId,
      path       : path,
      json       : json,
      valid_json : valid_json
   };

}

function Response( status, headers, body ){

   var setBody = function(arg){
      this.body = arg
   }

   var setStatus = function(arg){
      this.status = arg
   }

   var setHeaders = function(arg){
      this.headers = arg
   }

   var addHeader = function(property, value){
      this.headers[property] = value;
   }

   return {
      'status'     : status,
      'headers'    : headers,
      'body'       : (Object.prototype.toString.call(body) === '[object String]') ? body : JSON.stringify(body),
      'setBody'    : setBody,
      'setHeaders' : setHeaders,
      'setStatus'  : setStatus,
      'addHeader'  : addHeader
   }

}

function buildZmqResponse(uuid, connId, responseObj){
   return ''
      + uuid
      + ' '
      + String(connId).length
      + ':'
      + connId
      + ', '
      + buildHttpResponse(responseObj)
}

function buildHttpResponse(responseObj) {

   var status  = responseObj.status
   var headers = responseObj.headers
   var body    = responseObj.body

   var header_arr = []

   header_arr.push('Content-Length: ' + body.length)

   for (var header in headers) {
      if (__hasProp.call(headers, header)) {
         header_arr.push('' + header + ': ' + headers[header])
      }
   }

   return 'HTTP/1.1 '
      + status
      + ' '
      + statusMessages[status]
      + '\r\n'
      + header_arr.join('\r\n')
      + '\r\n\r\n'
      + body
}

function sender(config){

   dbc.hasMember(config, 'spec')
   dbc.hasMemberIn(config, 'type', ['pub', 'push'])

   var send      = zmq.socket(config.type);
   send.identity = config.id

   process.on('exit', function(){
      if (send){
         send.close()
         process.exit()
      }
   })

   if (config.bind){
      send.bindSync(config.spec);
   }
   else{
      send.connect(config.spec);
   }

   if (config.isMongrel2){
      return {
         'send' : function (uuid, connId, responseObj){
            var message = buildZmqResponse(uuid, connId, responseObj)
            return send.send(message)
         }
      }
   }
   else {
      return {
         'send' : function (message){
            send.send(JSON.stringify(message))
         }
      }
   }
}

function receiver(config, error_config, callback) {

   dbc.hasMember           (config, 'spec')
   dbc.hasMemberIn         (config, 'type', ['sub', 'pull'])
   dbc.conditionalHasMember(config, 'subscribe', config.type === 'sub')

   var receive      = zmq.socket(config.type)
   receive.identity = config.id

   var error         = null

   if (config.isMongrel2){
      dbc.hasMember  (error_config, 'spec')
      dbc.assert     (error_config.isMongrel2 === true)
      error = zmq.socket('pub')
      error.connect(error_config.spec)
   }

   process.on('exit', function(){
      if (receive){
         receive.close()
      }
      if (config.isMongrel2 && error){
         error.close();
      }
      process.exit()
   })

   if (config.isMongrel2){
      receive.on('message', function (envelope) {

         console.log(envelope.toString('utf8'))

         var zmqMsg = parse(envelope.toString('utf8'))

         if (zmqMsg.headers.METHOD === 'OPTIONS'){
            var optHeaders = {
               "access-control-allow-origin" : "*",
               "access-control-allow-methods": "GET, POST, PUT, DELETE, OPTIONS",
               "access-control-allow-headers": "content-type, accept",
               "access-control-max-age"      : 10, // Seconds.
               "content-length"              : 0
            }
            var responseObj = new Response(status.NO_CONTENT_204, optHeaders, '')
            var message     = buildZmqResponse(zmqMsg.uuid, zmqMsg.connId, responseObj)
            error.send(message)
         }
         else if (zmqMsg.valid_json){
            delete zmqMsg['valid_json']
            return callback(zmqMsg)
         }
         else{
            var responseObj = new Response(status.BAD_REQUEST_400, header_json, {'message':'Invalid JSON'})
            var message     = buildZmqResponse(zmqMsg.uuid, zmqMsg.connId, responseObj)
            error.send(message)
         }
      })
   }
   else{
      receive.on('message', function (envelope) {
         var zmqMsg = envelope.toString('utf8')
         return callback(JSON.parse(zmqMsg))
      })
   }

   if (config.bind){
      receive.bindSync(config.spec);
   }
   else{
      receive.connect(config.spec);
   }

   if ('sub' === config.type){
      receiver.subscribe(config.subscribe);
   }

   return receiver
}

module.exports.Response           = Response
module.exports.status             = status
module.exports.header_plain       = header_plain
module.exports.header_json        = header_json
module.exports.header_html        = header_html
module.exports.getJSON            = getJSON
module.exports.parse              = parse
module.exports.parseNetstring     = parseNetstring
module.exports.receiver           = receiver
module.exports.sender             = sender
module.exports.buildHttpResponse  = buildHttpResponse
module.exports.buildZmqResponse   = buildZmqResponse<commit_msg>remoced debug statement<commit_after>'use strict';

var zmq = require('zmq')
var dbc = require('dbc')

var __hasProp = Object.prototype.hasOwnProperty

var status = {
   OK_200                     : 200,
   NO_CONTENT_204             : 204,
   BAD_REQUEST_400            : 400,
   UNAUTHORIZED_401           : 401,
   FORBIDDEN_402              : 402,
   NOT_FOUND_404              : 404,
   INTERNAL_SERVER_ERROR_500  : 500
}

var header_plain = { "access-control-allow-origin" : '*', 'Content-Type': 'text/plain; charset=utf-8'       }
var header_json  = { "access-control-allow-origin" : '*', 'Content-Type': 'application/json; charset=utf-8' }
var header_html  = { "access-control-allow-origin" : '*', 'Content-Type': 'text/html; charset=utf-8'        }

var statusMessages = {
   '100': 'Continue',
   '101': 'Switching Protocols',
   '200': 'OK',
   '201': 'Created',
   '202': 'Accepted',
   '203': 'Non-Authoritative Information',
   '204': 'No Content',
   '205': 'Reset Content',
   '206': 'Partial Content',
   '300': 'Multiple Choices',
   '301': 'Moved Permanently',
   '302': 'Found',
   '303': 'See Other',
   '304': 'Not Modified',
   '305': 'Use Proxy',
   '307': 'Temporary Redirect',
   '400': 'Bad Request',
   '401': 'Unauthorized',
   '402': 'Payment Required',
   '403': 'Forbidden',
   '404': 'Not Found',
   '405': 'Method Not Allowed',
   '406': 'Not Acceptable',
   '407': 'Proxy Authentication Required',
   '408': 'Request Timeout',
   '409': 'Conflict',
   '410': 'Gone',
   '411': 'Length Required',
   '412': 'Precondition Failed',
   '413': 'Request Entity Too Large',
   '414': 'Request-URI Too Large',
   '415': 'Unsupported Media Type',
   '416': 'Request Range Not Satisfiable',
   '417': 'Expectation Failed',
   '500': 'Internal Server Error',
   '501': 'Not Implemented',
   '502': 'Bad Gateway',
   '503': 'Service Unavailable',
   '504': 'Gateway Timeout',
   '505': 'HTTP Version Not Supported'
}

function split(str, chr, limit) {
   var parts = str.split  (chr)
   var ret   = parts.slice(0, limit - 1)

   ret.push(parts.slice(limit - 1).join(chr))
   return ret
}

function parseNetstring(ns) {

   var nsSplit = split(ns, ':', 2)
   var length  = parseInt  (nsSplit[0], 10)
   var payload = nsSplit[1]

   if (payload[length] !== ',') {
      throw 'Netstring did not end in ","'
   }

   return [ payload.slice(0, length), payload.slice(length + 1) ]
}

function getJSON(str){
   try{
      return JSON.parse(str)
   }
   catch(e) {
      return null
   }
}

function isValidJSON(str){
   if (0 === str.length){
      return true
   }
   try{
      JSON.parse(str)
      return true;
   }
   catch(e) {
      return false
   }
}

function parse(mongrelString) {
   var mongrelStringSplit = split(mongrelString, ' ', 4)

   var uuid       = mongrelStringSplit[0]
   var connId     = mongrelStringSplit[1]
   var path       = mongrelStringSplit[2]
   var payload    = mongrelStringSplit[3]

   var paySplit   = parseNetstring(payload)
   var headers    = getJSON       (paySplit[0])
   var body       = parseNetstring(paySplit[1])[0]

   var valid_json = isValidJSON(body)
   var json       = getJSON(body)

   return {
      headers    : headers,
      uuid       : uuid,
      connId     : connId,
      path       : path,
      json       : json,
      valid_json : valid_json
   };

}

function Response( status, headers, body ){

   var setBody = function(arg){
      this.body = arg
   }

   var setStatus = function(arg){
      this.status = arg
   }

   var setHeaders = function(arg){
      this.headers = arg
   }

   var addHeader = function(property, value){
      this.headers[property] = value;
   }

   return {
      'status'     : status,
      'headers'    : headers,
      'body'       : (Object.prototype.toString.call(body) === '[object String]') ? body : JSON.stringify(body),
      'setBody'    : setBody,
      'setHeaders' : setHeaders,
      'setStatus'  : setStatus,
      'addHeader'  : addHeader
   }

}

function buildZmqResponse(uuid, connId, responseObj){
   return ''
      + uuid
      + ' '
      + String(connId).length
      + ':'
      + connId
      + ', '
      + buildHttpResponse(responseObj)
}

function buildHttpResponse(responseObj) {

   var status  = responseObj.status
   var headers = responseObj.headers
   var body    = responseObj.body

   var header_arr = []

   header_arr.push('Content-Length: ' + body.length)

   for (var header in headers) {
      if (__hasProp.call(headers, header)) {
         header_arr.push('' + header + ': ' + headers[header])
      }
   }

   return 'HTTP/1.1 '
      + status
      + ' '
      + statusMessages[status]
      + '\r\n'
      + header_arr.join('\r\n')
      + '\r\n\r\n'
      + body
}

function sender(config){

   dbc.hasMember(config, 'spec')
   dbc.hasMemberIn(config, 'type', ['pub', 'push'])

   var send      = zmq.socket(config.type);
   send.identity = config.id

   process.on('exit', function(){
      if (send){
         send.close()
         process.exit()
      }
   })

   if (config.bind){
      send.bindSync(config.spec);
   }
   else{
      send.connect(config.spec);
   }

   if (config.isMongrel2){
      return {
         'send' : function (uuid, connId, responseObj){
            var message = buildZmqResponse(uuid, connId, responseObj)
            return send.send(message)
         }
      }
   }
   else {
      return {
         'send' : function (message){
            send.send(JSON.stringify(message))
         }
      }
   }
}

function receiver(config, error_config, callback) {

   dbc.hasMember           (config, 'spec')
   dbc.hasMemberIn         (config, 'type', ['sub', 'pull'])
   dbc.conditionalHasMember(config, 'subscribe', config.type === 'sub')

   var receive      = zmq.socket(config.type)
   receive.identity = config.id

   var error         = null

   if (config.isMongrel2){
      dbc.hasMember  (error_config, 'spec')
      dbc.assert     (error_config.isMongrel2 === true)
      error = zmq.socket('pub')
      error.connect(error_config.spec)
   }

   process.on('exit', function(){
      if (receive){
         receive.close()
      }
      if (config.isMongrel2 && error){
         error.close();
      }
      process.exit()
   })

   if (config.isMongrel2){
      receive.on('message', function (envelope) {

         var zmqMsg = parse(envelope.toString('utf8'))

         if (zmqMsg.headers.METHOD === 'OPTIONS'){
            var optHeaders = {
               "access-control-allow-origin" : "*",
               "access-control-allow-methods": "GET, POST, PUT, DELETE, OPTIONS",
               "access-control-allow-headers": "content-type, accept",
               "access-control-max-age"      : 10, // Seconds.
               "content-length"              : 0
            }
            var responseObj = new Response(status.NO_CONTENT_204, optHeaders, '')
            var message     = buildZmqResponse(zmqMsg.uuid, zmqMsg.connId, responseObj)
            error.send(message)
         }
         else if (zmqMsg.valid_json){
            delete zmqMsg['valid_json']
            return callback(zmqMsg)
         }
         else{
            var responseObj = new Response(status.BAD_REQUEST_400, header_json, {'message':'Invalid JSON'})
            var message     = buildZmqResponse(zmqMsg.uuid, zmqMsg.connId, responseObj)
            error.send(message)
         }
      })
   }
   else{
      receive.on('message', function (envelope) {
         var zmqMsg = envelope.toString('utf8')
         return callback(JSON.parse(zmqMsg))
      })
   }

   if (config.bind){
      receive.bindSync(config.spec);
   }
   else{
      receive.connect(config.spec);
   }

   if ('sub' === config.type){
      receiver.subscribe(config.subscribe);
   }

   return receiver
}

module.exports.Response           = Response
module.exports.status             = status
module.exports.header_plain       = header_plain
module.exports.header_json        = header_json
module.exports.header_html        = header_html
module.exports.getJSON            = getJSON
module.exports.parse              = parse
module.exports.parseNetstring     = parseNetstring
module.exports.receiver           = receiver
module.exports.sender             = sender
module.exports.buildHttpResponse  = buildHttpResponse
module.exports.buildZmqResponse   = buildZmqResponse<|endoftext|>


<commit_before>"use strict";

const fs = require('fs');
const crypto = require('crypto');
const gm = require('gm');
const stream2buffer = require('stream-to-buffer');
const streamifier = require('streamifier');
const XRegExp = require('xregexp').XRegExp;

const IMAGE_REGEX = XRegExp('^\/ai\/(?<width>\\d+)x(?<height>\\d+)(?<x2>@2x)?\/(?<path>.+\\.(?<ext>jpe?g|png|gif))(?:\\?(?:.+)?)?$');
const MIME_TYPES = {
  'jpg': 'image/jpeg',
  'jpeg': 'image/jpeg',
  'png': 'image/png',
  'gif': 'image/gif'
};

module.exports = function(http) {
  let log = http.log.as('http:ai');
  let config = http.config.ai || {};

  let imagesDir = mkpath(sand.appPath, config.imagesDir);

  let put = config.put;
  let get = config.get;

  if (config.useMemcache && sand.memcache) {
    put = function(key, buf, callback) {
      sand.memcache.set(key, buf, config.cacheLife, callback);
    };
    get = function(key, callback) {
      sand.memcache.get(key, callback);
    };
  }

  return function(req, res, next) {

    // ignore if we're not GETing
    if ('GET' !== req.method) {
      return next();
    }

    // check if route matches
    let matches = IMAGE_REGEX.xexec(req.originalUrl);

    if (!matches) {
      return next();
    }

    let width = parseInt(matches.width);
    let height = parseInt(matches.height);

    // for 2x images
    if (matches.x2 || req.query.x2) {
      width *= 2;
      height *= 2;
    }

    let path = matches.path;
    let mimetype = MIME_TYPES[matches.ext];

    // we don't know how to return this one
    if (!mimetype) {
      return next();
    }

    if (!config.useCache) {
      return adapt(function(err, stdout) {

        if (err) {
          log(err.message || err);
          return next(err);
        }

        send(stdout);

      });
    }

    getCache(function(err, buffer) {
      if (err) {
        log(err.message || err);
        return next(err);
      }

      if (buffer) {
        let stream = streamifier.createReadStream(buffer);
        return send(stream);
      }

      putCache(function(err) {
        if (err) {
          log(err.message || err);
          return next(err);
        }

        adapt(function(err, stdout) {

          if (err) {
            log(err.message || err);
            return next(err)
          }

          getCache(function(err, buffer) {

            if (err) {
              log(err.message || err);
              return next(err);
            }

            if (buffer) {
              let stream = streamifier.createReadStream(buffer);
              return send(stream);
            }

            res.send(404);

          });

        });

      });

    });

    adapt(function(err, stdout, stderr) {

      if (err) {
        log(err.message || err);
        return next(err);
      }

      send(stdout);

    });

    // Helper functions

    function send(stream) {
      res.setHeader('content-type', mimetype);
      stream.pipe(res);
    }

    function adapt(callback) {
      /*
       * For Graphics Magick docs: http://www.graphicsmagick.org/GraphicsMagick.html#details-geometry
       * Inspiration: http://stackoverflow.com/questions/14705152/thumbnails-from-graphics-magick-without-upscaling
       */
      gm(mkpath(imagesDir, path))
        .resize(`${width}^`, `${height}^`)
        .gravity('Center')
        .extent(width, height)
        .stream(callback);
    }

    function getCache(callback) {
      get(getKey(), callback);
    }

    function putCache(callback) {
      let key = getKey();
      adapt(function(err, stdout, stderr) {
        if (err) {
          return callback(err);
        }

        stream2buffer(stdout, function(err, buffer) {
          if (err) {
            return callback(buffer);
          }

          put(key, buffer, callback);
        });
      });
    }

    function getKey() {
      return width + 'x' + height + '-' + path + '-' + crypto.createHash('sha1').update(width + 'x' + height + '-' + path).digest('hex');
    }

  };

};

function mkpath(dir, rel) {
  return dir.replace(/\/$/, '') + '/' + rel.replace(/^\//, '');
}<commit_msg>improved ai<commit_after>"use strict";

const fs = require('fs');
const crypto = require('crypto');
const gm = require('gm');
const stream2buffer = require('stream-to-buffer');
const streamifier = require('streamifier');
const XRegExp = require('xregexp').XRegExp;
const co = require('co');

const IMAGE_REGEX = XRegExp('^\/ai\/(?<width>\\d+)x(?<height>\\d+)(?<x2>@2x)?\/(?<path>.+\\.(?<ext>jpe?g|png|gif))(?:\\?(?:.+)?)?$');
const MIME_TYPES = {
  'jpg': 'image/jpeg',
  'jpeg': 'image/jpeg',
  'png': 'image/png',
  'gif': 'image/gif'
};

module.exports = function(http) {
  let log = http.log.as('http:ai');
  let config = http.config.ai || {};

  let imagesDir = mkpath(sand.appPath, config.imagesDir);

  let put = config.put;
  let get = config.get;

  if (config.useMemcache && sand.memcache) {
    put = function(key, buf, callback) {
      sand.memcache.set(key, buf, config.cacheLife, callback);
    };
    get = function(key, callback) {
      sand.memcache.get(key, callback);
    };
  }

  return function(req, res, next) {

    // ignore if we're not GETing
    if ('GET' !== req.method) {
      return next();
    }

    // check if route matches
    let matches = IMAGE_REGEX.xexec(req.originalUrl);

    if (!matches) {
      return next();
    }

    // collect the adapted dimensions
    let width = parseInt(matches.width);
    let height = parseInt(matches.height);

    let is2x = matches.x2 || req.query.x2;

    // for 2x images
    if (is2x) {
      width *= 2;
      height *= 2;
    }

    let path = matches.path;
    let mimetype = MIME_TYPES[matches.ext];

    // we don't know how to return this one
    if (!mimetype) {
      return next();
    }

    if (!config.useCache) {
      return adapt(function(err, stdout) {

        if (err) {
          log(err.message || err);
          return next(err);
        }

        send(stdout);

      });
    }

    co(function *() {

      try {

        // check if we have cache
        var buffer = yield getCache();

        if (buffer) {
          // if we do, then return it right away
          let stream = streamifier.createReadStream(buffer);
          return send(stream);
        }

        // otherwise, make a buffer and cache it
        buffer = yield putCache();

        if (!buffer) {
          // we should never get here!!! buffer should always be returned
          return next(new Error('AI: Unable to retrieve image! (' + getKey() + ')'));
        }

        // change buffer to stream and return
        let stream = streamifier.createReadStream(buffer);
        return send(stream);

      } catch(e) {
        http.log(e.message || e);
        return next(e);
      }

    });

    // Helper functions

    function send(stream) {
      res.setHeader('content-type', mimetype);
      stream.pipe(res);
    }

    /**
     * Adapts the image to the proper dimensions with cropping
     *
     * @returns {Promise}
     */
    function adapt(callback) {
      /*
       * For Graphics Magick docs: http://www.graphicsmagick.org/GraphicsMagick.html#details-geometry
       * Inspiration: http://stackoverflow.com/questions/14705152/thumbnails-from-graphics-magick-without-upscaling
       */

      gm(mkpath(imagesDir, path))
        .resize(`${width}^`, `${height}^`)
        .gravity('Center')
        .extent(width, height)
        .stream(function(err, stdout, stderr) {
          if (err) {
            return callback(err);
          }
          callback(null, stdout);
        });
    }

    /**
     * Fetches an image buffer from the cache
     *
     * @returns {Promise}
     */
    function getCache() {
      return new Promise(function(resolve, reject) {
        // create and fetch the cache key
        get(getKey(), function(err, data) {
          if (err) {
            return reject(err);
          }
          resolve(data);
        });
      });
    }

    /**
     * Creates an adapted image buffer | puts it in the cache | returns the buffer
     *
     * @returns {Promise}
     */
    function putCache() {
      return new Promise(function(resolve, reject) {
        // make the cache key
        let key = getKey();

        // adapt the image
        adapt(function(err, stdout, stderr) {
          if (err) {
            return reject(err);
          }

          // convert the new image stream to buffer
          stream2buffer(stdout, function(err, buffer) {
            if (err) {
              return reject(err);
            }

            // put the buffer in the cache
            put(key, buffer, function(err, data) {
              if (err) {
                return reject(err);
              }

              // return the buffer
              resolve(buffer);
            });
          });
        });
      });
    }

    /**
     * Builds a cache key for the adapted image
     *
     * @returns {string}
     */
    function getKey() {
      let paramString = width + 'x' + height + '-' + path + (is2x ? '-@2x' : '');
      return paramString + '-' + crypto.createHash('sha1').update(paramString).digest('hex');
    }

  };

};

function mkpath(dir, rel) {
  return dir.replace(/\/$/, '') + '/' + rel.replace(/^\//, '');
}<|endoftext|>


<commit_before>/**++++++++++++++
*  NeDB Database
*++++++++++++++++
*
*
The MIT License (MIT)

Copyright (c) 2014 Equan Pr.

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*
*/
var Datastore = require('nedb'),
    util = require('../util'),
    db = new Datastore({
        filename: util.root()+'/db/pagedek.db',
        autoload: true
    });

module.exports = db;<commit_msg>fix 'root' doesn't exist.<commit_after>/**++++++++++++++
*  NeDB Database
*++++++++++++++++
*
*
The MIT License (MIT)

Copyright (c) 2014 Equan Pr.

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*
*/
var Datastore = require('nedb'),
    root = require('../util'),
    db = new Datastore({
        filename: root.path()+'/db/pagedek.db',
        autoload: true
    });

module.exports = db;<|endoftext|>


<commit_before>'use strict';

var util = require('util');
var HttpResult = require('../session/http_result');
var EventEmitter = require('events').EventEmitter;

util.inherits(Cursor, EventEmitter);

var _handleError = function(err, obj) {
  if (err) throw err;
}

function Cursor(obj, session, route, query, segmentKey) {
  this._obj = obj;
  this._session = session;
  this._route = route;
  this._query = query;
  this._segmentKey = segmentKey || "data";
}

Cursor.prototype.run = function() {
  var base = this;
  try {
    this._session.get(this._route, JSON.stringify(this._query), _handleError, function(result) {
      if (result.code == HttpResult.OK) {
        var json = JSON.parse(result.body);
        var payload = json[base._segmentKey];

        payload.forEach(function(item) {
          base.emit('data', base._obj.fromJSON(item));
        });

        base.emit('end');
        return {};
      }
    });
  } catch (e) {
    base.emit('error', e);
  }
}

Cursor.prototype.toArray = function(callback) {
  var buf = [];
  this.on('data', function(item) {
    buf.push(item);
  }).on('end', function() {
    callback(null, buf);
  }).on('error', function(e) {
    callback(e, null);
  });
}

module.exports = Cursor;
<commit_msg>cursoring implementation<commit_after>'use strict';

var util = require('util');
var HttpResult = require('../session/http_result');
var EventEmitter = require('events').EventEmitter;

util.inherits(Cursor, EventEmitter);

var _handleError = function(err, obj) {
  if (err) throw err;
}

function Cursor(obj, session, route, query, segmentKey) {
  this._obj = obj;
  this._session = session;
  this._route = route;
  this._query = query;
  this._segmentKey = segmentKey || "data";
}

Cursor.prototype._handleRequest = function(base, result) {
  if (result.code == HttpResult.OK) {
    var json = JSON.parse(result.body);
    var payload = json[base._segmentKey];

    if (json.cursor) {
      payload.forEach(function(item) {
        base.emit('data', base._obj.fromJSON(item));
      });
      base._loadFromServer(base, json.cursor.next_query)
      return {};
    } else {
      payload.forEach(function(item) {
        base.emit('data', base._obj.fromJSON(item));
      });
      base.emit('end');
      return {};
    }
  }
}

Cursor.prototype._loadFromServer = function(base, body) {
  var payload = JSON.stringify(body) || JSON.stringify(base._query)
  base._session.get(base._route, JSON.stringify(payload), _handleError, function(result) {
    base._handleRequest(base, result)
    return {}
  });
}

Cursor.prototype.run = function() {
  var base = this;
  try {
    base._loadFromServer(base)
  } catch (e) {
    base.emit('error', e);
  }
}

Cursor.prototype.toArray = function(callback) {
  var buf = [];
  this.on('data', function(item) {
    buf.push(item);
  }).on('end', function() {
    callback(null, buf);
  }).on('error', function(e) {
    callback(e, null);
  });
}

module.exports = Cursor;
<|endoftext|>


<commit_before>var _ = require('lodash')

module.exports = function parseOptions (url, options, callback) {
  var opts = {}
  if (_.isString(url)) {
    opts.url = url
  } else {
    _.extend(opts, url)
  }
  _.each([options, callback], function (fn) {
    if (_.isFunction(fn)) {
      opts.success = fn
    }
  })
  if (!_.isFunction(options)) {
    _.extend(opts, options)
  }
  return opts
}
<commit_msg>simplify options parser a bit more<commit_after>var _ = require('lodash')

module.exports = function parseOptions (url, options, callback) {
  var opts = {}
  if (_.isString(url)) {
    opts.url = url
  } else {
    _.extend(opts, url)
  }
  if (_.isFunction(options)) {
    opts.success = options
  } else {
    if (_.isFunction(callback)) opts.success = callback
    _.extend(opts, options)
  }
  return opts
}
<|endoftext|>