<commit_before>var inflect = require('i')()
  , Adapter = require('./adapter')
  , route = require('./route')
  , express = require('express')
  , RSVP = require('rsvp')
  , _ = require('lodash');

/*!
 * The Fortune object.
 */
function Fortune() {
  this._init.apply(this, arguments);
}

/**
 * An object that is passed in to the Fortune constructor, which contains all of the configuration options.
 *
 * ### Database setup
 * - `adapter`: may be either "nedb", "mongodb", "mysql", "psql", "sqlite", or an adapter object. Default: "nedb".
 * - `db`: the name of the database to use. Default: "fortune".
 * - `host`: the address of the database machine. Default: "localhost".
 * - `port`: the port of the database machine. Do not set this unless you do not plan on using the default port for the database.
 * - `username`: username for logging into the database. This may be optional for MongoDB.
 * - `password`: password for logging into the database. This may be optional for MongoDB.
 * - `flags`: an object containing additional options to pass to the adapter.
 *
 * ### Fortune setup
 * - `baseUrl`: if this is set, then your API gets upgraded to URL-style JSON API.
 * - `namespace`: optional namespace for your API, i.e. `api/v1`.
 * - `suffix`: optional suffix to every route, for example, `/posts.json`, `/posts/1.json`, `/posts/1/comments.json`.
 * - `cors`: boolean value indicating whether or not to enable Cross Origin Resource Sharing (CORS), or an object that contains additional configuration keys: `headers` (Array), `methods` (Array), `origins` (Array), and `credentials` (Boolean). Default: true.
 * - `production`: boolean value indicating whether or not to strip spaces from responses. Default: false.
 *
 * *Note: in order to use database adapters, you must install `fortune-mongodb` for MongoDB, or `fortune-relational` for relational databases.*
 */
Fortune.prototype.options = {};

/**
 * Default application settings.
 *
 * @api private
 */
Fortune.prototype._defaults = {

  // database setup
  adapter: 'nedb',
  host: 'localhost',
  port: null,
  db: 'fortune',
  username: '',
  password: '',
  flags: {},

  // fortune options
  baseUrl: '',
  namespace: '',
  suffix: '',
  cors: true,
  production: false

};

/**
 * Constructor method.
 *
 * @api private
 * @param {Object} options
 */
Fortune.prototype._init = function(options) {
  var router;

  // Initialize options.
  options = typeof options === 'object' ? options : {};
  for(var key in this._defaults) {
    if(!options.hasOwnProperty(key))
      options[key] = this._defaults[key];
  }
  this.options = options;

  // Create the underlying express framework instance.
  this.router = express();
  router = this.router;

  // Setup express.
  if(typeof options.cors === 'boolean' || typeof options.cors === 'object' && options.cors) {
    router.use(allowCrossDomain(options.cors));
  }
  router.disable('x-powered-by');
  router.use(express.json());

  // Create a database adapter instance.
  this.adapter = new Adapter(options);
};

/**
 * Define a resource and setup routes simultaneously. A schema field may be either a native type, a plain object, or a string that refers to a related resource.
 *
 * Valid native types: `String`, `Number`, `Boolean`, `Date`, `Array`, `Buffer`
 *
 * Alternatively, the object format must be as follows:
 *
 * ```javascript
 * {type: String} // no association
 * {ref: 'relatedResource', inverse: 'relatedKey'} // "belongs to" association to "relatedKey" key on "relatedResource"
 * [{ref: 'anotherResource', inverse: 'someKey'}] // "has many" association to "someKey" on "anotherResource"
 * ```
 *
 * @param {String} name the name of the resource
 * @param {Object} schema the schema object to add
 * @param {Object} options additional options to pass to the schema
 * @return {this}
 */
Fortune.prototype.resource = function(name, schema, options) {
  var _this = this;

  this._resource = name;
  if(typeof schema != 'object') return this;
  if(this.adapter.model(name)) {
    console.warn('Warning: resource "' + name + '" was already defined.');
    return this;
  }

  this.adapter.awaitConnection().then(function() {
    schema = _this._scrubSchema(schema);

    // Store a copy of the input.
    _this._schema[name] = _.clone(schema);

    try {
      schema = _this.adapter.schema(name, schema, options);
      _this._route(name, _this.adapter.model(name, schema));
    } catch(error) {
      console.trace('There was an error loading the "' + name + '" resource. ' + error);
    }
  }, function(error) {
    throw error;
  });
  return this;
};

/**
 * Scrub keys on a schema before passing it off to the adapter.
 *
 * @api private
 * @param {Object} schema
 * @return {Object}
 */
Fortune.prototype._scrubSchema = function(schema) {
  ['id', 'href', 'links'].forEach(function(reservedKey) {
    if(schema.hasOwnProperty(reservedKey)) {
      delete schema[reservedKey];
      console.warn('Reserved key "' + reservedKey + '" is not allowed.');
    }
  });
  return schema;
};

/**
 * Internal method to add transforms on a resource.
 *
 * @api private
 * @param {String} name
 * @param {Function} fn
 * @param {String} stage
 */
Fortune.prototype._addTransform = function(name, fn, stage) {
  var _this = this;

  if(typeof name === 'function') {
    fn = name;
    name = this._resource;
  }
  if(typeof fn === 'function') {
    name.split(' ').forEach(function(key) {
      _this[stage][key] = fn;
    });
  }
};

/**
 * Do something before a resource is saved in the database.
 * The callback function has one parameter, the request object.
 * It may return either the resource or a promise. Here's a contrived
 * example that stores the Authorization header into a resource:
 *
 * ```javascript
 * app.before('resource', function(request) {
 *   var authorization = request.get('Authorization');
 *   if(!authorization) throw new Error('Authorization failed');
 *   this.authorization = authorization;
 *   return this;
 * });
 * ```
 *
 * @param {String} name may be space separated, i.e. 'cat dog human'
 * @param {Function} fn this callback function is called within the context of the resource, and has one parameter: the request object.
 * @return {this}
 */
Fortune.prototype.before = function(name, fn) {
  this._addTransform(name, fn, '_before');
  return this;
};

/**
 * Do something after a resource is read from the database.
 * The callback function has one parameter, the request object.
 * It may return either the resource or a promise. Here's a contrived
 * example that hides a `password` and `salt` from being exposed:
 *
 * ```javascript
 * app.after('user', function() {
 *   delete this.password;
 *   delete this.salt;
 *   return this;
 * });
 * ```
 *
 * @param {String} name may be space separated, i.e. 'cat dog human'
 * @param {Function} fn this callback function is called within the context of the resource, and has one parameter: the request object.
 * @return {this}
 */
Fortune.prototype.after = function(name, fn) {
  this._addTransform(name, fn, '_after');
  return this;
};

/**
 * Convenience method to define before & after at once.
 *
 * @param {String} [name] if no name is passed, the last defined resource is used
 * @param {Function} before see "before" method
 * @param {Function} after see "after" method
 * @return {this}
 */
Fortune.prototype.transform = function(name, before, after) {
  if(arguments.length < 3) {
    after = before;
    before = name;
    name = this._resource;
  }
  this.before(name, before);
  this.after(name, after);
  return this;
};

/**
 * This accepts a `connect` middleware function. For more information, [here is a guide for how to write connect middleware](http://stephensugden.com/middleware_guide/).
 *
 * @param {Function} fn connect middleware
 * @return {this}
 */
Fortune.prototype.use = function() {
  var router = this.router;
  router.use.apply(router, arguments);
  return this;
};

/**
 * Start the API by listening on the specified port.
 *
 * @param {Number} port the port number to use
 * @return {this}
 */
Fortune.prototype.listen = function() {
  var router = this.router;
  router.listen.apply(router, arguments);
  console.log('A fortune is available on port ' + arguments[0] + '...');
  return this;
};

/**
 * Internal method to remove HTTP routes from a resource.
 *
 * @api private
 * @param {String} name
 * @param {Array} methods
 * @param {Array} [routes]
 */
Fortune.prototype._removeRoutes = function(name, methods, routes) {
  var router = this.router
    , collection = inflect.pluralize(name)
    , re = new RegExp('\/' + collection);

  this.adapter.awaitConnection().then(function() {
    (methods || []).forEach(function(verb) {
      var paths = router.routes[verb];
      paths.forEach(function(route, index) {
        if(routes ? _.contains(routes, route.path) : re.test(route.path)) {
          paths.splice(index, 1);
        }
      });
    });
  });
};

/**
 * Mark a resource as read-only, which destroys the routes
 * for `POST`, `PUT`, `PATCH`, and `DELETE` on that resource. The resource
 * can still be modified using adapter methods.
 *
 * @param {String} [name] if no name is passed, the last defined resource is used.
 * @return {this}
 */
Fortune.prototype.readOnly = function(name) {
  if(typeof name != 'string') name = this._resource;
  this._removeRoutes(name, ['post', 'put', 'patch', 'delete']);
  return this;
};

/**
 * Mark a resource as not having an index, which destroys the `GET` index.
 *
 * @param {String} [name] if no name is passed, the last defined resource is used.
 * @return {this}
 */
Fortune.prototype.noIndex = function(name) {
  if(typeof name != 'string') name = this._resource;
  var index = this.options.namespace + '/' + inflect.pluralize(name);
  this._removeRoutes(name, ['get'], [index]);
  return this;
};

/**
 * Namespace for the router, which is actually an instance of `express`.
 */
Fortune.prototype.router = {};

/**
 * Namespace for the adapter.
 */
Fortune.prototype.adapter = {};

/**
 * Store loaded schemas here.
 *
 * @api private
 */
Fortune.prototype._schema = {};

/**
 * Store methods to transform input.
 *
 * @api private
 */
Fortune.prototype._before = {};

/**
 * Store methods to transform output.
 *
 * @api private
 */
Fortune.prototype._after = {};

/**
 * Method to route a resource.
 *
 * @api private
 */
Fortune.prototype._route = route;

/**
 * Keep track of the last added resource so that we can
 * chain methods that act on resources.
 *
 * @api private
 */
Fortune.prototype._resource = '';

// Default Cross-Origin Resource Sharing setup.
function allowCrossDomain(cors) {

  var headers = cors.headers || ['Accept', 'Content-Type', 'Authorization', 'X-Requested-With'],
      methods = cors.methods || ['GET', 'PUT', 'POST', 'PATCH', 'DELETE'],
      origins = cors.origins || '*',
      credentials = cors.credentials || true;

  return function(req, res, next) {
    var origin = req.get('Origin');

    if(!origin) return next();

    if(origins !== '*') {
      if(origins.indexOf(origin)) {
        origins = origin;
      } else {
        next();
      }
    }

    res.header('Access-Control-Allow-Origin', origins);
    res.header('Access-Control-Allow-Headers', headers.join(', '));
    res.header('Access-Control-Allow-Methods', methods.join(', '));
    res.header('Access-Control-Allow-Credentials', credentials.toString());
    // intercept OPTIONS method
    if(req.method === 'OPTIONS') {
      res.send(200);
    } else {
      next();
    }
  };
}

/*!
 * Create instance of Fortune.
 *
 * @param {Object} options
 */
function create(options) {
  return new Fortune(options);
}

// Expose create method
exports = module.exports = create;

// Expose Express framework
exports.express = express;

// Expose RSVP promise library
exports.RSVP = RSVP;

// Expose Lodash
exports._ = _;
<commit_msg>update docs<commit_after>var inflect = require('i')()
  , Adapter = require('./adapter')
  , route = require('./route')
  , express = require('express')
  , RSVP = require('rsvp')
  , _ = require('lodash');

/*!
 * The Fortune object.
 */
function Fortune() {
  this._init.apply(this, arguments);
}

/**
 * An object that is passed in to the Fortune constructor, which contains all of the configuration options.
 *
 * ### Database setup
 * - `adapter`: may be either "nedb", "mongodb", "mysql", "psql", "sqlite", or an adapter object. Default: "nedb".
 * - `db`: the name of the database to use. Default: "fortune".
 * - `host`: the address of the database machine. Default: "localhost".
 * - `port`: the port of the database machine. Do not set this unless you do not plan on using the default port for the database.
 * - `username`: username for logging into the database. This may be optional for MongoDB.
 * - `password`: password for logging into the database. This may be optional for MongoDB.
 * - `flags`: an object containing additional options to pass to the adapter.
 *
 * ### Fortune setup
 * - `baseUrl`: if this is set, then your API gets upgraded to URL-style JSON API.
 * - `namespace`: optional namespace for your API, i.e. `api/v1`.
 * - `suffix`: optional suffix to every route, for example, `/posts.json`, `/posts/1.json`, `/posts/1/comments.json`.
 * - `cors`: boolean value indicating whether or not to enable Cross Origin Resource Sharing (CORS), or an object that contains additional configuration keys: `headers` (Array), `methods` (Array), `origins` (Array), and `credentials` (Boolean). Default: true.
 * - `production`: boolean value indicating whether or not to strip spaces from responses. Default: false.
 *
 * *Note: in order to use database adapters, you must install `fortune-mongodb` for MongoDB, or `fortune-relational` for relational databases.*
 */
Fortune.prototype.options = {};

/**
 * Default application settings.
 *
 * @api private
 */
Fortune.prototype._defaults = {

  // database setup
  adapter: 'nedb',
  host: 'localhost',
  port: null,
  db: 'fortune',
  username: '',
  password: '',
  flags: {},

  // fortune options
  baseUrl: '',
  namespace: '',
  suffix: '',
  cors: true,
  production: false

};

/**
 * Constructor method.
 *
 * @api private
 * @param {Object} options
 */
Fortune.prototype._init = function(options) {
  var router;

  // Initialize options.
  options = typeof options === 'object' ? options : {};
  for(var key in this._defaults) {
    if(!options.hasOwnProperty(key))
      options[key] = this._defaults[key];
  }
  this.options = options;

  // Create the underlying express framework instance.
  this.router = express();
  router = this.router;

  // Setup express.
  if(typeof options.cors === 'boolean' || typeof options.cors === 'object' && options.cors) {
    router.use(allowCrossDomain(options.cors));
  }
  router.disable('x-powered-by');
  router.use(express.json());

  // Create a database adapter instance.
  this.adapter = new Adapter(options);
};

/**
 * Define a resource and setup routes simultaneously. A schema field may be either a native type, a plain object, or a string that refers to a related resource.
 *
 * Valid native types: `String`, `Number`, `Boolean`, `Date`, `Array`, `Buffer`
 *
 * Alternatively, the object format must be as follows:
 *
 * ```javascript
 * {type: String} // no association
 * {ref: 'relatedResource', inverse: 'relatedKey'} // "belongs to" association to "relatedKey" key on "relatedResource"
 * [{ref: 'anotherResource', inverse: 'someKey'}] // "has many" association to "someKey" on "anotherResource"
 * ```
 *
 * @param {String} name the name of the resource
 * @param {Object} schema the schema object to add
 * @param {Object} options additional options to pass to the schema
 * @return {this}
 */
Fortune.prototype.resource = function(name, schema, options) {
  var _this = this;

  this._resource = name;
  if(typeof schema != 'object') return this;
  if(this.adapter.model(name)) {
    console.warn('Warning: resource "' + name + '" was already defined.');
    return this;
  }

  this.adapter.awaitConnection().then(function() {
    schema = _this._scrubSchema(schema);

    // Store a copy of the input.
    _this._schema[name] = _.clone(schema);

    try {
      schema = _this.adapter.schema(name, schema, options);
      _this._route(name, _this.adapter.model(name, schema));
    } catch(error) {
      console.trace('There was an error loading the "' + name + '" resource. ' + error);
    }
  }, function(error) {
    throw error;
  });
  return this;
};

/**
 * Scrub keys on a schema before passing it off to the adapter.
 *
 * @api private
 * @param {Object} schema
 * @return {Object}
 */
Fortune.prototype._scrubSchema = function(schema) {
  ['id', 'href', 'links'].forEach(function(reservedKey) {
    if(schema.hasOwnProperty(reservedKey)) {
      delete schema[reservedKey];
      console.warn('Reserved key "' + reservedKey + '" is not allowed.');
    }
  });
  return schema;
};

/**
 * Internal method to add transforms on a resource.
 *
 * @api private
 * @param {String} name
 * @param {Function} fn
 * @param {String} stage
 */
Fortune.prototype._addTransform = function(name, fn, stage) {
  var _this = this;

  if(typeof name === 'function') {
    fn = name;
    name = this._resource;
  }
  if(typeof fn === 'function') {
    name.split(' ').forEach(function(key) {
      _this[stage][key] = fn;
    });
  }
};

/**
 * Do something before a resource is saved in the database.
 * The callback function has two optional parameters, the request and response objects, respectively.
 * It may return either the resource or a promise. Here's a contrived
 * example that stores the Authorization header into a resource:
 *
 * ```javascript
 * app.before('resource', function(request, response) {
 *   var authorization = request.get('Authorization');
 *   if(!authorization) throw new Error('Authorization failed');
 *   this.authorization = authorization;
 *   return this;
 * });
 * ```
 *
 * @param {String} name may be space separated, i.e. 'cat dog human'
 * @param {Function} fn this callback function is called within the context of the resource, and has two optional parameters: the request and response objects, respectively.
 * @return {this}
 */
Fortune.prototype.before = function(name, fn) {
  this._addTransform(name, fn, '_before');
  return this;
};

/**
 * Do something after a resource is read from the database.
 * The callback function has two optional parameters, the request and response objects, respectively.
 * It may return either the resource or a promise. Here's a contrived
 * example that hides a `password` and `salt` from being exposed:
 *
 * ```javascript
 * app.after('user', function() {
 *   delete this.password;
 *   delete this.salt;
 *   return this;
 * });
 * ```
 *
 * @param {String} name may be space separated, i.e. 'cat dog human'
 * @param {Function} fn this callback function is called within the context of the resource, and has two optional parameters: the request and response objects, respectively.
 * @return {this}
 */
Fortune.prototype.after = function(name, fn) {
  this._addTransform(name, fn, '_after');
  return this;
};

/**
 * Convenience method to define before & after at once.
 *
 * @param {String} [name] if no name is passed, the last defined resource is used
 * @param {Function} before see "before" method
 * @param {Function} after see "after" method
 * @return {this}
 */
Fortune.prototype.transform = function(name, before, after) {
  if(arguments.length < 3) {
    after = before;
    before = name;
    name = this._resource;
  }
  this.before(name, before);
  this.after(name, after);
  return this;
};

/**
 * This accepts a `connect` middleware function. For more information, [here is a guide for how to write connect middleware](http://stephensugden.com/middleware_guide/).
 *
 * @param {Function} fn connect middleware
 * @return {this}
 */
Fortune.prototype.use = function() {
  var router = this.router;
  router.use.apply(router, arguments);
  return this;
};

/**
 * Start the API by listening on the specified port.
 *
 * @param {Number} port the port number to use
 * @return {this}
 */
Fortune.prototype.listen = function() {
  var router = this.router;
  router.listen.apply(router, arguments);
  console.log('A fortune is available on port ' + arguments[0] + '...');
  return this;
};

/**
 * Internal method to remove HTTP routes from a resource.
 *
 * @api private
 * @param {String} name
 * @param {Array} methods
 * @param {Array} [routes]
 */
Fortune.prototype._removeRoutes = function(name, methods, routes) {
  var router = this.router
    , collection = inflect.pluralize(name)
    , re = new RegExp('\/' + collection);

  this.adapter.awaitConnection().then(function() {
    (methods || []).forEach(function(verb) {
      var paths = router.routes[verb];
      paths.forEach(function(route, index) {
        if(routes ? _.contains(routes, route.path) : re.test(route.path)) {
          paths.splice(index, 1);
        }
      });
    });
  });
};

/**
 * Mark a resource as read-only, which destroys the routes
 * for `POST`, `PUT`, `PATCH`, and `DELETE` on that resource. The resource
 * can still be modified using adapter methods.
 *
 * @param {String} [name] if no name is passed, the last defined resource is used.
 * @return {this}
 */
Fortune.prototype.readOnly = function(name) {
  if(typeof name != 'string') name = this._resource;
  this._removeRoutes(name, ['post', 'put', 'patch', 'delete']);
  return this;
};

/**
 * Mark a resource as not having an index, which destroys the `GET` index.
 *
 * @param {String} [name] if no name is passed, the last defined resource is used.
 * @return {this}
 */
Fortune.prototype.noIndex = function(name) {
  if(typeof name != 'string') name = this._resource;
  var index = this.options.namespace + '/' + inflect.pluralize(name);
  this._removeRoutes(name, ['get'], [index]);
  return this;
};

/**
 * Namespace for the router, which is actually an instance of `express`.
 */
Fortune.prototype.router = {};

/**
 * Namespace for the adapter.
 */
Fortune.prototype.adapter = {};

/**
 * Store loaded schemas here.
 *
 * @api private
 */
Fortune.prototype._schema = {};

/**
 * Store methods to transform input.
 *
 * @api private
 */
Fortune.prototype._before = {};

/**
 * Store methods to transform output.
 *
 * @api private
 */
Fortune.prototype._after = {};

/**
 * Method to route a resource.
 *
 * @api private
 */
Fortune.prototype._route = route;

/**
 * Keep track of the last added resource so that we can
 * chain methods that act on resources.
 *
 * @api private
 */
Fortune.prototype._resource = '';

// Default Cross-Origin Resource Sharing setup.
function allowCrossDomain(cors) {

  var headers = cors.headers || ['Accept', 'Content-Type', 'Authorization', 'X-Requested-With'],
      methods = cors.methods || ['GET', 'PUT', 'POST', 'PATCH', 'DELETE'],
      origins = cors.origins || '*',
      credentials = cors.credentials || true;

  return function(req, res, next) {
    var origin = req.get('Origin');

    if(!origin) return next();

    if(origins !== '*') {
      if(origins.indexOf(origin)) {
        origins = origin;
      } else {
        next();
      }
    }

    res.header('Access-Control-Allow-Origin', origins);
    res.header('Access-Control-Allow-Headers', headers.join(', '));
    res.header('Access-Control-Allow-Methods', methods.join(', '));
    res.header('Access-Control-Allow-Credentials', credentials.toString());
    // intercept OPTIONS method
    if(req.method === 'OPTIONS') {
      res.send(200);
    } else {
      next();
    }
  };
}

/*!
 * Create instance of Fortune.
 *
 * @param {Object} options
 */
function create(options) {
  return new Fortune(options);
}

// Expose create method
exports = module.exports = create;

// Expose Express framework
exports.express = express;

// Expose RSVP promise library
exports.RSVP = RSVP;

// Expose Lodash
exports._ = _;
<|endoftext|>


<commit_before>/*! fpo.js
	v0.0.14 (c) Kyle Simpson
	MIT License: http://getify.mit-license.org
*/

(function UMD(name,context,definition){
	if (typeof define === "function" && define.amd) { define(definition); }
	else if (typeof module !== "undefined" && module.exports) { module.exports = definition(); }
	else { context[name] = definition(name,context); }
})("FPO",this,function DEF(name,context){
	"use strict";

	var publicAPI = {
		identity,
		constant,
		pick: curryMultiple( {fn: pick, n: 2} ),
		pickAll: curryMultiple( {fn: pickAll, n: 2} ),
		unary: curryMultiple( {fn: unary, n: 2} ),
		binary: curryMultiple( {fn: binary, n: 2} ),
		curry,
		curryMultiple,
		uncurry,
		partial: curryMultiple( {fn: partial, n: 2 } ),
		complement,
		apply,
		gather: curryMultiple( {fn: gather, n: 2} ),
		compose,
		pipe,
		prop: curryMultiple( {fn: prop, n: 2} ),
		setProp: curryMultiple( {fn: setProp, n: 3} ),
		filterIn: curryMultiple( {fn: filterIn, n: 2} ),
		filterOut: curryMultiple( {fn: filterOut, n: 2} ),
		map: curryMultiple( {fn: map, n: 2} ),
		reduce: curryMultiple( {fn: reduce, n: 3} ),
		reduceRight: curryMultiple( {fn: reduceRight, n: 3} ),
		flatten,
		zip: curryMultiple( {fn: zip, n: 2} ),
		flatMap: curryMultiple( {fn: flatMap, n: 2} ),
		trampoline,
	};

	publicAPI.std = {
		identity: gather( {fn: identity, props: ["v"]} ),
		constant: gather( {fn: constant, props: ["v"]} ),
		pick: gather( {fn: pick, props: ["props","o"]} ),
		pickAll: gather( {fn: pickAll, props: ["props","o"]} ),
		unary: gather( {fn: unary, props: ["fn","prop"]} ),
		binary: gather( {fn: binary, props: ["fn","props"]} ),
		curry: stdCurry,
		curryMultiple: stdCurryMultiple,
		uncurry: stdUncurry,
		partial: stdCurryMultiple( _partial, /*arity=*/2 ),
		complement: gather( {fn: complement, props: ["fn"]} ),
		apply: stdCurryMultiple( gather( {fn: apply, props: ["fn","props"]} ), /*arity=*/1 ),
		gather: stdCurryMultiple( gather( {fn: gather, props: ["fn","props"]} ), /*arity=*/2 ),
		compose: gather( {fn: compose, props: ["fns"]} ),
		pipe: gather( {fn: pipe, props: ["fns"]} ),
		prop: stdCurryMultiple( gather( {fn: prop, props: ["name","o"]} ), /*arity=*/2 ),
		setProp: stdCurryMultiple( gather( {fn: setProp, props: ["name","o","v"]} ), /*arity=*/3 ),
		filterIn: stdCurryMultiple( gather( {fn: filterIn, props: ["fn","arr"]} ), /*arity=*/2 ),
		filterOut: stdCurryMultiple( gather( {fn: filterOut, props: ["fn","arr"]} ), /*arity=*/2 ),
		map: stdCurryMultiple( gather( {fn: map, props: ["fn","arr"]} ), /*arity=*/2 ),
		reduce: stdCurryMultiple( gather( {fn: reduce, props: ["fn","v","arr"]} ), /*arity=*/3 ),
		reduceRight: stdCurryMultiple( gather( {fn: reduceRight, props: ["fn","v","arr"]} ), /*arity=*/3 ),
		flatten: stdCurryMultiple( gather( {fn: flatten, props: ["arr","n"]} ), /*arity=*/1 ),
		zip: stdCurryMultiple( gather( {fn: zip, props: ["arr1","arr2"]} ), /*arity=*/2 ),
		flatMap: stdCurryMultiple( gather( {fn: flatMap, props: ["fn","arr"]}), /*arity=*/2 ),
		trampoline: gather( {fn: trampoline, props: ["fn"]} ),
	};

	// method convenience aliases
	_setMethodAlias("constant","always");
	_setMethodAlias("pipe","flow");
	_setMethodAlias("gather","unapply");
	_setMethodAlias("compose","flowRight");
	_setMethodAlias("setProp","assoc");
	_setMethodAlias("reduce","fold");
	_setMethodAlias("reduce","foldL");
	_setMethodAlias("reduceRight","foldR");
	_setMethodAlias("flatMap","chain");

	return publicAPI;

	// ***************************************

	function identity({ v } = {}) {
		return v;
	}

	function constant({ v } = {}) {
		return function value(){
			return v;
		};
	}

	function pick({ o: obj, props = [] } = {}) {
		return props.reduce( function addPropToObj(newObj,key){
			if (key in obj) newObj[key] = obj[key];
			return newObj;
		}, {} );
	}

	function pickAll({ o: obj, props = [] } = {}) {
		return props.reduce( function addPropToObj(newObj,key){
			newObj[key] = obj[key];
			return newObj;
		}, {} );
	}

	function unary({ fn, prop: propName1 } = {}) {
		return function onlyOneArg(argObj = {}){
			return fn( pickAll( {o: argObj, props: [propName1]} ) );
		};
	}

	function binary({ fn, props: [ propName1, propName2 ] = [] } = {}) {
		return function onlyTwoArgs(argsObj = {}){
			return fn( pickAll( {o: argsObj, props: [propName1,propName2]} ) );
		};
	}

	function curry({ fn, n: arity = 1 } = {}) {
		return (function nextCurried(prevArgsObj){
			return function curried(nextArgObj = {}){
				var [key] = Object.keys( nextArgObj );
				var allArgsObj = Object.assign( {}, prevArgsObj, { [key]: nextArgObj[key] } );

				if (Object.keys( allArgsObj ).length >= arity) {
					return fn( allArgsObj );
				}
				else {
					return nextCurried( allArgsObj );
				}
			};
		})( {} );
	}

	function stdCurry(fn,arity = fn.length) {
		return (function nextCurried(prevArgs){
			return function curried(nextArg){
				var args = prevArgs.concat( [nextArg] );

				if (args.length >= arity) {
					return fn( ...args );
				}
				else {
					return nextCurried( args );
				}
			};
		})( [] );
	}

	function curryMultiple({ fn, n: arity = 1 } = {}) {
		return (function nextCurried(prevArgsObj){
			return function curried(nextArgsObj = {}){
				var [key] = Object.keys( nextArgsObj );
				var argsObj = Object.assign( {}, prevArgsObj, nextArgsObj );

				if (Object.keys( argsObj ).length >= arity) {
					return fn( argsObj );
				}
				else {
					return nextCurried( argsObj );
				}
			};
		})( {} );
	}

	function stdCurryMultiple(fn,arity = fn.length) {
		return (function nextCurried(prevArgs){
			return function curried(...nextArgs){
				var args = prevArgs.concat( nextArgs );

				if (args.length >= arity) {
					return fn( ...args );
				}
				else {
					return nextCurried( args );
				}
			};
		})( [] );
	}

	function uncurry({ fn } = {}) {
		return function uncurried(argsObj = {}){
			var ret = fn;

			for (let prop of Object.keys( argsObj )) {
				// assume `fn` is strictly curried (needs props one at a time),
				// instead of loose/multiple currying
				ret = ret( { [prop]: argsObj[prop] } );
			}

			return ret;
		};
	}

	function stdUncurry(fn) {
		return function uncurried(...args){
			var ret = fn;

			for (let i = 0; i < args.length; i++) {
				// assume `fn` is strictly curried (needs one arg at a time),
				// instead of loose/multiple currying
				ret = ret( args[i] );
			}

			return ret;
		};
	}

	function partial({ fn, args: partialArgsObj = {} } = {}) {
		return function partiallyApplied(restArgsObj = {}){
			return fn( Object.assign( {}, partialArgsObj, restArgsObj ) );
		};
	}

	function complement({ fn: predicateFn } = {}) {
		return function complemented(...args){
			return !predicateFn( ...args );
		};
	}

	function apply({
		fn,
		props: propNamesInOrder = fn.toString()
			.replace( /^(?:(?:function.*\(([^]*?)\))|(?:([^\(\)]+?)\s*=>)|(?:\(([^]*?)\)\s*=>))[^]+$/, "$1$2$3" )
			.split( /\s*,\s*/ )
			.map( v => v.replace( /[=\s].*$/, "" ) )
	} = {}) {
		return function appliedFn(argsObj) {
			return fn( ...propNamesInOrder.map( function getPropVal(k) { return argsObj[k]; } ) );
		};
	}

	function gather({ fn, props: propNamesInOrder = [] } = {}) {
		return function gatheredFn(...args) {
			return fn(
				zip( {arr1: propNamesInOrder, arr2: args} )
				.reduce( function addPropToObj(obj,[key,val]){
					obj[key] = val;
					return obj;
				}, {} )
			);
		};
	}

	function compose({ fns = [] } = {}) {
		return function composed(result){
			// copy the array of functions to prevent side effects
			var list = fns.slice();

			while (list.length > 0) {
				result = list.pop()( result );
			}

			return result;
		};
	}

	function pipe({ fns = [] } = {}) {
		return function piped(result){
			// copy the array of functions to prevent side effects
			var list = fns.slice();

			while (list.length > 0) {
				result = list.shift()( result );
			}

			return result;
		};
	}

	function prop({ name = "v", o = {} } = {}) {
		return o[name];
	}

	function setProp({ name = "v", o = {}, v } = {}) {
		var clone = Object.assign( {}, o );
		clone[name] = v;
		return clone;
	}

	function filterIn({ fn: predicateFn, arr = [] } = {}) {
		return arr.filter( predicateFn );
	}

	function filterOut({ fn: predicateFn, arr = [] } = {}) {
		return filterIn( complement( predicateFn ), arr );
	}

	function map({ fn: mapperFn, arr = [] } ={}) {
		return arr.map( mapperFn );
	}

	function reduce({ fn: reducerFn, v: initialValue, arr = [] } = {}) {
		return initialValue !== undefined ?
			arr.reduce( reducerFn, initialValue ) :
			arr.reduce( reducerFn );
	}

	function reduceRight({ fn: reducerFn, v: initialValue, arr = [] } = {}) {
		return initialValue !== undefined ?
			arr.reduceRight( reducerFn, initialValue ) :
			arr.reduceRight( reducerFn );
	}

	function flatten({ arr = [], n: depth = Infinity } = {}) {
		return arr.reduce(function reducer(list,v){
			return list.concat(
				depth > 0 ?
					(depth > 1 && Array.isArray( v ) ?
						flatten( {arr: v, n: depth - 1} ) :
						v
					) :
					[v]
			);
		}, [] );
	}

	function zip({ arr1 = [], arr2 = [] } = {}) {
		var zipped = [];
		arr1 = arr1.slice();
		arr2 = arr2.slice();

		while (arr1.length > 0 && arr2.length > 0) {
			zipped.push( [ arr1.shift(), arr2.shift() ] );
		}

		return zipped;
	}

	function flatMap({ fn: mapperFn, arr = [] } = {}) {
		return arr.reduce( function reducer(list,v) {
			list.push( mapperFn( v ) );
			return list;
		}, [] );
	}

	function trampoline({ fn } = {}) {
		return function trampolined(...args) {
			var result = fn( ...args );

			while (typeof result == "function") {
				result = result();
			}

			return result;
		};
	}

	// ***************************************
	// Private

	function _partial(fn,presetArgs = []) {
		return function partiallyApplied(...laterArgs){
			return fn( ...presetArgs, ...laterArgs );
		};
	}

	function _setMethodAlias(origName,aliasName) {
		publicAPI[aliasName] = publicAPI[origName];
		publicAPI.std[aliasName] = publicAPI.std[origName];
	}

});
<commit_msg>fixing std list operations (map/filter/reduce/flatMap)<commit_after>/*! fpo.js
	v0.0.15 (c) Kyle Simpson
	MIT License: http://getify.mit-license.org
*/

(function UMD(name,context,definition){
	if (typeof define === "function" && define.amd) { define(definition); }
	else if (typeof module !== "undefined" && module.exports) { module.exports = definition(); }
	else { context[name] = definition(name,context); }
})("FPO",this,function DEF(name,context){
	"use strict";

	var publicAPI = {
		identity,
		constant,
		pick: curryMultiple( {fn: pick, n: 2} ),
		pickAll: curryMultiple( {fn: pickAll, n: 2} ),
		unary: curryMultiple( {fn: unary, n: 2} ),
		binary: curryMultiple( {fn: binary, n: 2} ),
		curry,
		curryMultiple,
		uncurry,
		partial: curryMultiple( {fn: partial, n: 2 } ),
		complement,
		apply,
		gather: curryMultiple( {fn: gather, n: 2} ),
		compose,
		pipe,
		prop: curryMultiple( {fn: prop, n: 2} ),
		setProp: curryMultiple( {fn: setProp, n: 3} ),
		filterIn: curryMultiple( {fn: filterIn, n: 2} ),
		filterOut: curryMultiple( {fn: filterOut, n: 2} ),
		map: curryMultiple( {fn: map, n: 2} ),
		flatMap: curryMultiple( {fn: flatMap, n: 2} ),
		reduce: curryMultiple( {fn: reduce, n: 2} ),
		reduceRight: curryMultiple( {fn: reduceRight, n: 2} ),
		flatten,
		zip: curryMultiple( {fn: zip, n: 2} ),
		trampoline,
	};

	publicAPI.std = {
		identity: gather( {fn: identity, props: ["v"]} ),
		constant: gather( {fn: constant, props: ["v"]} ),
		pick: gather( {fn: pick, props: ["props","o"]} ),
		pickAll: gather( {fn: pickAll, props: ["props","o"]} ),
		unary: gather( {fn: unary, props: ["fn","prop"]} ),
		binary: gather( {fn: binary, props: ["fn","props"]} ),
		curry: stdCurry,
		curryMultiple: stdCurryMultiple,
		uncurry: stdUncurry,
		partial: stdCurryMultiple( _partial, /*arity=*/2 ),
		complement: gather( {fn: complement, props: ["fn"]} ),
		apply: stdCurryMultiple( gather( {fn: apply, props: ["fn","props"]} ), /*arity=*/1 ),
		gather: stdCurryMultiple( gather( {fn: gather, props: ["fn","props"]} ), /*arity=*/2 ),
		compose: gather( {fn: compose, props: ["fns"]} ),
		pipe: gather( {fn: pipe, props: ["fns"]} ),
		prop: stdCurryMultiple( gather( {fn: prop, props: ["name","o"]} ), /*arity=*/2 ),
		setProp: stdCurryMultiple( gather( {fn: setProp, props: ["name","o","v"]} ), /*arity=*/3 ),
		filterIn: stdCurryMultiple( gather( {fn: _applyFnProp( filterIn, ["v","i","arr"] ), props: ["fn","arr"]} ), /*arity=*/2 ),
		filterOut: stdCurryMultiple( gather( {fn: _applyFnProp( filterOut, ["v","i","arr"] ), props: ["fn","arr"]} ), /*arity=*/2 ),
		map: stdCurryMultiple( gather( {fn: _applyFnProp( map, ["v","i","arr"] ), props: ["fn","arr"]} ), /*arity=*/2 ),
		flatMap: stdCurryMultiple( gather( {fn: _applyFnProp( flatMap, ["v","i","arr"] ), props: ["fn","arr"]}), /*arity=*/2 ),
		reduce: stdCurryMultiple( gather( {fn: _applyFnProp( reduce, ["acc","v","i","arr"] ), props: ["fn","v","arr"]} ), /*arity=*/3 ),
		reduceRight: stdCurryMultiple( gather( {fn: _applyFnProp( reduceRight, ["acc","v","i","arr"] ), props: ["fn","v","arr"]} ), /*arity=*/3 ),
		flatten: stdCurryMultiple( gather( {fn: flatten, props: ["arr","n"]} ), /*arity=*/1 ),
		zip: stdCurryMultiple( gather( {fn: zip, props: ["arr1","arr2"]} ), /*arity=*/2 ),
		trampoline: gather( {fn: trampoline, props: ["fn"]} ),
	};

	// method convenience aliases
	_setMethodAlias("constant","always");
	_setMethodAlias("pipe","flow");
	_setMethodAlias("gather","unapply");
	_setMethodAlias("compose","flowRight");
	_setMethodAlias("setProp","assoc");
	_setMethodAlias("filterIn","filter");
	_setMethodAlias("reduce","fold");
	_setMethodAlias("reduce","foldL");
	_setMethodAlias("reduceRight","foldR");
	_setMethodAlias("flatMap","chain");

	return publicAPI;

	// ***************************************

	function identity({ v } = {}) {
		return v;
	}

	function constant({ v } = {}) {
		return function value(){
			return v;
		};
	}

	function pick({ o: obj, props = [] } = {}) {
		return props.reduce( function addPropToObj(newObj,key){
			if (key in obj) newObj[key] = obj[key];
			return newObj;
		}, {} );
	}

	function pickAll({ o: obj, props = [] } = {}) {
		return props.reduce( function addPropToObj(newObj,key){
			newObj[key] = obj[key];
			return newObj;
		}, {} );
	}

	function unary({ fn, prop: propName1 } = {}) {
		return function onlyOneArg(argObj = {}){
			return fn( pickAll( {o: argObj, props: [propName1]} ) );
		};
	}

	function binary({ fn, props: [ propName1, propName2 ] = [] } = {}) {
		return function onlyTwoArgs(argsObj = {}){
			return fn( pickAll( {o: argsObj, props: [propName1,propName2]} ) );
		};
	}

	function curry({ fn, n: arity = 1 } = {}) {
		return (function nextCurried(prevArgsObj){
			return function curried(nextArgObj = {}){
				var [key] = Object.keys( nextArgObj );
				var allArgsObj = Object.assign( {}, prevArgsObj, { [key]: nextArgObj[key] } );

				if (Object.keys( allArgsObj ).length >= arity) {
					return fn( allArgsObj );
				}
				else {
					return nextCurried( allArgsObj );
				}
			};
		})( {} );
	}

	function stdCurry(fn,arity = fn.length) {
		return (function nextCurried(prevArgs){
			return function curried(nextArg){
				var args = prevArgs.concat( [nextArg] );

				if (args.length >= arity) {
					return fn( ...args );
				}
				else {
					return nextCurried( args );
				}
			};
		})( [] );
	}

	function curryMultiple({ fn, n: arity = 1 } = {}) {
		return (function nextCurried(prevArgsObj){
			return function curried(nextArgsObj = {}){
				var [key] = Object.keys( nextArgsObj );
				var argsObj = Object.assign( {}, prevArgsObj, nextArgsObj );

				if (Object.keys( argsObj ).length >= arity) {
					return fn( argsObj );
				}
				else {
					return nextCurried( argsObj );
				}
			};
		})( {} );
	}

	function stdCurryMultiple(fn,arity = fn.length) {
		return (function nextCurried(prevArgs){
			return function curried(...nextArgs){
				var args = prevArgs.concat( nextArgs );

				if (args.length >= arity) {
					return fn( ...args );
				}
				else {
					return nextCurried( args );
				}
			};
		})( [] );
	}

	function uncurry({ fn } = {}) {
		return function uncurried(argsObj = {}){
			var ret = fn;

			for (let prop of Object.keys( argsObj )) {
				// assume `fn` is strictly curried (needs props one at a time),
				// instead of loose/multiple currying
				ret = ret( { [prop]: argsObj[prop] } );
			}

			return ret;
		};
	}

	function stdUncurry(fn) {
		return function uncurried(...args){
			var ret = fn;

			for (let i = 0; i < args.length; i++) {
				// assume `fn` is strictly curried (needs one arg at a time),
				// instead of loose/multiple currying
				ret = ret( args[i] );
			}

			return ret;
		};
	}

	function partial({ fn, args: partialArgsObj = {} } = {}) {
		return function partiallyApplied(restArgsObj = {}){
			return fn( Object.assign( {}, partialArgsObj, restArgsObj ) );
		};
	}

	function complement({ fn: predicateFn } = {}) {
		return function complemented(...args){
			return !predicateFn( ...args );
		};
	}

	function apply({
		fn,
		props: propNamesInOrder = fn.toString()
			.replace( /^(?:(?:function.*\(([^]*?)\))|(?:([^\(\)]+?)\s*=>)|(?:\(([^]*?)\)\s*=>))[^]+$/, "$1$2$3" )
			.split( /\s*,\s*/ )
			.map( v => v.replace( /[=\s].*$/, "" ) )
	} = {}) {
		return function appliedFn(argsObj) {
			return fn( ...propNamesInOrder.map( function getPropVal(k) { return argsObj[k]; } ) );
		};
	}

	function gather({ fn, props: propNamesInOrder = [] } = {}) {
		return function gatheredFn(...args) {
			return fn(
				zip( {arr1: propNamesInOrder, arr2: args} )
				.reduce( function addPropToObj(obj,[key,val]){
					obj[key] = val;
					return obj;
				}, {} )
			);
		};
	}

	function compose({ fns = [] } = {}) {
		return function composed(result){
			// copy the array of functions to prevent side effects
			var list = fns.slice();

			while (list.length > 0) {
				result = list.pop()( result );
			}

			return result;
		};
	}

	function pipe({ fns = [] } = {}) {
		return function piped(result){
			// copy the array of functions to prevent side effects
			var list = fns.slice();

			while (list.length > 0) {
				result = list.shift()( result );
			}

			return result;
		};
	}

	function prop({ name = "v", o = {} } = {}) {
		return o[name];
	}

	function setProp({ name = "v", o = {}, v } = {}) {
		var clone = Object.assign( {}, o );
		clone[name] = v;
		return clone;
	}

	function filterIn({ fn: predicateFn, arr = [] } = {}) {
		var newArr = [];
		for (let [i,v] of arr.entries()) {
			if (predicateFn( { v, i, arr } )) {
				newArr[newArr.length] = v;
			}
		}
		return newArr;
	}

	function filterOut({ fn: predicateFn, arr = [] } = {}) {
		return filterIn( {fn: complement( {fn: predicateFn} ), arr} );
	}

	function map({ fn: mapperFn, arr = [] } = {}) {
		var newArr = [];
		for (let [i,v] of arr.entries()) {
			newArr[i] = mapperFn( {v, i, arr} );
		}
		return newArr;
	}

	function flatMap({ fn: mapperFn, arr = [] } = {}) {
		var newArr = [];
		for (let [i,v] of arr.entries()) {
			newArr = newArr.concat( mapperFn( {v, i, arr} ) );
		}
		return newArr;
	}

	function reduce({ fn: reducerFn, v: initialValue, arr = [] } = {}) {
		var origArr = arr;
		var i = 0;

		if (initialValue === undefined) {
			if (arr.length == 0) return;

			initialValue = arr[0];
			arr = arr.slice( 1 );
			i++;
		}
		for (let v of arr) {
			initialValue = reducerFn( {acc: initialValue, v, i: i++, arr: origArr} );
		}
		return initialValue;
	}

	function reduceRight({ fn: reducerFn, v: initialValue, arr = [] } = {}) {
		var origArr = arr;
		var i = arr.length - 1;

		if (initialValue === undefined) {
			if (arr.length == 0) return;

			initialValue = arr[i];
			arr = arr.slice( 0, i );
			i--;
		}
		for (let v of [...arr].reverse()) {
			initialValue = reducerFn( {acc: initialValue, v, i: i--, arr: origArr} );
		}
		return initialValue;
	}

	function flatten({ arr = [], n: depth = Infinity } = {}) {
		return arr.reduce(function reducer(list,v){
			return list.concat(
				depth > 0 ?
					(depth > 1 && Array.isArray( v ) ?
						flatten( {arr: v, n: depth - 1} ) :
						v
					) :
					[v]
			);
		}, [] );
	}

	function zip({ arr1 = [], arr2 = [] } = {}) {
		var zipped = [];
		arr1 = arr1.slice();
		arr2 = arr2.slice();

		while (arr1.length > 0 && arr2.length > 0) {
			zipped.push( [ arr1.shift(), arr2.shift() ] );
		}

		return zipped;
	}

	function trampoline({ fn } = {}) {
		return function trampolined(...args) {
			var result = fn( ...args );

			while (typeof result == "function") {
				result = result();
			}

			return result;
		};
	}

	// ***************************************
	// Private

	function _partial(fn,presetArgs = []) {
		return function partiallyApplied(...laterArgs){
			return fn( ...presetArgs, ...laterArgs );
		};
	}

	function _setMethodAlias(origName,aliasName) {
		publicAPI[aliasName] = publicAPI[origName];
		publicAPI.std[aliasName] = publicAPI.std[origName];
	}

	function _applyFnProp(fn,props) {
		return function fnApplied(argsObj){
			argsObj = Object.assign( {}, argsObj );
			argsObj.fn = apply( {fn: argsObj.fn, props} );
			return fn( argsObj );
		};
	}

});
<|endoftext|>


<commit_before>var netlib = require("net"),
    EventEmitter = require('events').EventEmitter,
    utillib = require("util");

module.exports = Gearman;

function Gearman(host, port, debug){
    EventEmitter.call(this);

    this.debug = !!debug;
    this.port = port || 4730;
    this.host = host || "localhost";

    this.connected = this.connecting = this.processing = false;

    this.processing = false;
    this.commandQueue = [];
    this.handleCallbackQueue = [];

    this.remainder = false;

    this.currentJobs = {};
    this.currentWorkers = {};

    this.workers = {};
}
utillib.inherits(Gearman, EventEmitter);

Gearman.packetTypes = {
    CAN_DO: 1,
    CANT_DO: 2,
    RESET_ABILITIES: 3,
    PRE_SLEEP: 4,
    NOOP: 6,
    SUBMIT_JOB: 7,
    JOB_CREATED: 8,
    GRAB_JOB: 9,
    NO_JOB: 10,
    JOB_ASSIGN: 11,
    WORK_STATUS: 12,
    WORK_COMPLETE: 13,
    WORK_FAIL: 14,
    GET_STATUS: 15,
    ECHO_REQ: 16,
    ECHO_RES: 17,
    SUBMIT_JOB_BG: 18,
    ERROR: 19,
    STATUS_RES: 20,
    SUBMIT_JOB_HIGH: 21,
    SET_CLIENT_ID: 22,
    CAN_DO_TIMEOUT: 23,
    ALL_YOURS: 24,
    WORK_EXCEPTION: 25,
    OPTION_REQ: 26,
    OPTION_RES: 27,
    WORK_DATA: 28,
    WORK_WARNING: 29,
    GRAB_JOB_UNIQ: 30,
    JOB_ASSIGN_UNIQ: 31,
    SUBMIT_JOB_HIGH_BG: 32,
    SUBMIT_JOB_LOW: 33,
    SUBMIT_JOB_LOW_BG: 34,
    SUBMIT_JOB_SCHED: 35,
    SUBMIT_JOB_EPOCH: 36
};

Gearman.packetTypesReversed = {
    "1": "CAN_DO",
    "2": "CANT_DO",
    "3": "RESET_ABILITIES",
    "4": "PRE_SLEEP",
    "6": "NOOP",
    "7": "SUBMIT_JOB",
    "8": "JOB_CREATED",
    "9": "GRAB_JOB",
    "10": "NO_JOB",
    "11": "JOB_ASSIGN",
    "12": "WORK_STATUS",
    "13": "WORK_COMPLETE",
    "14": "WORK_FAIL",
    "15": "GET_STATUS",
    "16": "ECHO_REQ",
    "17": "ECHO_RES",
    "18": "SUBMIT_JOB_BG",
    "19": "ERROR",
    "20": "STATUS_RES",
    "21": "SUBMIT_JOB_HIGH",
    "22": "SET_CLIENT_ID",
    "23": "CAN_DO_TIMEOUT",
    "24": "ALL_YOURS",
    "25": "WORK_EXCEPTION",
    "26": "OPTION_REQ",
    "27": "OPTION_RES",
    "28": "WORK_DATA",
    "29": "WORK_WARNING",
    "30": "GRAB_JOB_UNIQ",
    "31": "JOB_ASSIGN_UNIQ",
    "32": "SUBMIT_JOB_HIGH_BG",
    "33": "SUBMIT_JOB_LOW",
    "34": "SUBMIT_JOB_LOW_BG",
    "35": "SUBMIT_JOB_SCHED",
    "36": "SUBMIT_JOB_EPOCH"
};

Gearman.paramCount = {
    ERROR:          ["string", "string"],
    JOB_ASSIGN:     ["string", "string", "buffer"],
    JOB_ASSIGN_UNIQ:["string", "string", "string", "buffer"],
    JOB_CREATED:    ["string"],
    WORK_COMPLETE:  ["string", "buffer"],
    WORK_EXCEPTION: ["string", "string"],
    WORK_WARNING:   ["string", "string"],
    WORK_DATA:      ["string", "buffer"],
    WORK_FAIL:      ["string"],
    WORK_STATUS:    ["string", "number", "number"]
};

Gearman.prototype.connect = function(){

    if(this.connected || this.connecting){
        // juhul kui ühendus on juba olemas käivita protsessimine
        if(this.connected && !this.processing){
            this.processCommandQueue();
        }
        return false;
    }

    this.connecting = true;

    if(this.debug){
        console.log("connecting...");
    }

    this.socket = netlib.createConnection(this.port, this.host);

    this.socket.on("connect", (function(){

        this.connecting = false;
        this.connected = true;

        if(this.debug){
            console.log("connected!");
        }
        this.emit("connect");

        this.processCommandQueue();

    }).bind(this));

    this.socket.on("end", this.close.bind(this));
    this.socket.on("close", this.close.bind(this));
    this.socket.on("timeout", this.close.bind(this));

    this.socket.on("error", this.errorHandler.bind(this));

    this.socket.on("data", this.receive.bind(this));
};

Gearman.prototype.close = function(){
    if(this.connected){
        this.closeConnection();
        this.emit("close");
    }
};

Gearman.prototype.closeConnection = function(){
    var i;

    if(this.connected){
        if(this.socket){
            try{
                this.socket.end();
            }catch(E){}
        }
        this.connected = false;
        this.connecting = false;

        // clear current jobs
        for(i in this.currentJobs){
            if(this.currentJobs.hasOwnProperty(i)){
                if(this.currentJobs[i]){
                    this.currentJobs[i].emit("error", new Error("Job failed"));
                }
                delete this.currentJobs[i];
            }
        }

        // clear current workers
        for(i in this.currentWorkers){
            if(this.currentWorkers.hasOwnProperty(i)){
                delete this.currentWorkers[i];
            }
        }
    }
};

Gearman.prototype.errorHandler = function(err){
    this.emit("error", err);
    this.closeConnection();
};

Gearman.prototype.processCommandQueue = function(chunk){
    var command;
    if(this.commandQueue.length){
        this.processing = true;
        command = this.commandQueue.shift();
        this.sendCommandToServer.apply(this, command);
    }else{
        this.processing = false;
    }
};

Gearman.prototype.sendCommand = function(){
    var command = Array.prototype.slice.call(arguments);
    this.commandQueue.push(command);
    if(!this.processing){
        this.processCommandQueue();
    }
};

Gearman.prototype.sendCommandToServer = function(){
    var body,
        args = Array.prototype.slice.call(arguments),
        commandName, commandId, commandCallback,
        i, len, bodyLength = 0, curpos = 12;

    if(!this.connected){
        this.commandQueue.unshift(args);
        return this.connect();
    }

    commandName = (args.shift() || "").trim().toUpperCase();

    if(args.length && typeof args[args.length-1] == "function"){
        commandCallback = args.pop();
        this.handleCallbackQueue.push(commandCallback);
    }

    commandId = Gearman.packetTypes[commandName] || 0;

    if(!commandId){
        // TODO: INVALID COMMAND!
    }

    for(i=0, len=args.length; i<len; i++){
        if(!(args[i] instanceof Buffer)){
            args[i] = new Buffer((args[i] || "").toString(), "utf-8");
        }
        bodyLength += args[i].length;
    }

    bodyLength += args.length>1 ? args.length - 1 : 0;

    body = new Buffer(bodyLength + 12); // packet size + 12 byte header

    body.writeUInt32BE(0x00524551, 0); // \0REQ
    body.writeUInt32BE(commandId, 4); // \0REQ
    body.writeUInt32BE(bodyLength, 8); // \0REQ

    // compose body
    for(i=0, len = args.length; i<len; i++){
        args[i].copy(body, curpos);
        curpos += args[i].length;
        if(i < args.length-1){
            body[curpos++] = 0x00;
        }
    }

    if(this.debug){
        console.log("Sending: " + commandName + " with "+args.length+" params");
        console.log(" - ", body);
        args.forEach(function(arg, i){
            console.log("  - ARG:"+i+" ", arg.toString());
        });
    }

    this.socket.write(body, this.processCommandQueue.bind(this));
};

Gearman.prototype.receive = function(chunk){
    var data = new Buffer((chunk && chunk.length || 0) + (this.remainder && this.remainder.length || 0)),
        commandId, commandName,
        bodyLength = 0, args = [], argTypes, curarg, i, len, argpos, curpos;

    // nothing to do here
    if(!data.length){
        return;
    }

    // if theres a remainder value, tie it together with the incoming chunk
    if(this.remainder){
        this.remainder.copy(data, 0, 0);
        if(chunk){
            chunk.copy(data, this.remainder.length, 0);
        }
    }else{
        data = chunk;
    }

    // response header needs to be at least 12 bytes
    // otherwise keep the current chunk as remainder
    if(data.length<12){
        this.remainder = data;
        return;
    }

    if(data.readUInt32BE(0) != 0x00524553){
        // OUT OF SYNC!
        return this.errorHandler(new Error("Out of sync with server"));
    }

    // response needs to be 12 bytes + payload length
    bodyLength = data.readUInt32BE(8);
    if(data.length < 12 + bodyLength){
        this.remainder = data;
        return;
    }

    // keep the remainder if incoming data is larger than needed
    if(data.length > 12 + bodyLength){
        this.remainder = data.slice(12 + bodyLength);
        data = data.slice(0, 12 + bodyLength);
    }else{
        this.remainder = false;
    }

    commandId = data.readUInt32BE(4);
    commandName = (Gearman.packetTypesReversed[commandId] || "");
    if(!commandName){
        // TODO: UNKNOWN COMMAND!
        return;
    }

    if(bodyLength && (argTypes = Gearman.paramCount[commandName])){
        curpos = 12;
        argpos = 12;

        for(i = 0, len = argTypes.length; i < len; i++){

            if(i < len - 1){
                while(data[curpos] !== 0x00 && curpos < data.length){
                    curpos++;
                }
                curarg = data.slice(argpos, curpos);
            }else{
                curarg = data.slice(argpos);
            }

            switch(argTypes[i]){
                case "string":
                    curarg = curarg.toString("utf-8");
                    break;
                case "number":
                    curarg = Number(curarg.toString()) || 0;
                    break;
            }

            args.push(curarg);

            curpos++;
            argpos = curpos;
            if(curpos >= data.length){
                break;
            }
        }
    }

    if(this.debug){
        console.log("Received: " + commandName + " with " + args.length + " params");
        console.log(" - ", data);
        args.forEach(function(arg, i){
            console.log("  - ARG:"+i+" ", arg.toString());
        });
    }

    // Run command
    if(typeof this["receive_" + commandName] == "function"){
        if(commandName == "JOB_CREATED" && this.handleCallbackQueue.length){
            args = args.concat(this.handleCallbackQueue.shift());
        }
        this["receive_" + commandName].apply(this, args);
    }

    // rerun receive just in case there's enough data for another command
    if(this.remainder && this.remainder.length>=12){
        process.nextTick(this.receive.bind(this));
    }
};

Gearman.prototype.receive_NO_JOB = function(){
    this.sendCommand("PRE_SLEEP");
    this.emit("idle");
};

Gearman.prototype.receive_NOOP = function(){
    this.sendCommand("GRAB_JOB");
};

Gearman.prototype.receive_ECHO_REQ = function(payload){
    this.sendCommand("ECHO_RES", payload);
};

Gearman.prototype.receive_ERROR = function(code, message){
    if(debug){
        console.log("Server error: ", code, message);
    }
};

Gearman.prototype.receive_JOB_CREATED = function(handle, callback){
    if(typeof callback == "function"){
        callback(handle);
    }
};

Gearman.prototype.receive_WORK_FAIL = function(handle){
    if(this.currentJobs[handle]){
        this.currentJobs[handle].emit("error", new Error("Job failed"));
        delete this.currentJobs[handle];
    }
};

Gearman.prototype.receive_WORK_DATA = function(handle, payload){
    if(this.currentJobs[handle]){
        this.currentJobs[handle].emit("data", payload);
    }
};

Gearman.prototype.receive_WORK_COMPLETE = function(handle, payload){
    if(this.currentJobs[handle]){
        if(payload){
            this.currentJobs[handle].emit("data", payload);
        }
        this.currentJobs[handle].emit("end");
        delete this.currentJobs[handle];
    }
};

Gearman.prototype.receive_JOB_ASSIGN = function(handle, name, payload){
    if(typeof this.workers[name] == "function"){
        var worker = new this.Worker(this, handle, name, payload);
        this.currentWorkers[handle] = worker;
        this.workers[name](payload, worker);
    }
};

Gearman.prototype.registerWorker = function(name, func){
    if(!this.workers[name]){
        this.sendCommand("CAN_DO", name);
        this.sendCommand("GRAB_JOB");
    }
    this.workers[name] = func;
};

Gearman.prototype.submitJob = function(name, payload){
    return new this.Job(this, name, payload);
};

// WORKER

Gearman.prototype.Worker = function(gearman, handle, name, payload){
    this.gearman = gearman;
    this.handle = handle;
    this.name = name;
    this.payload = payload;
    this.finished = false;
};

Gearman.prototype.Worker.prototype.write = function(data){
    if(this.finished){
        return;
    }
    this.gearman.sendCommand("WORK_DATA", this.handle, data);
};

Gearman.prototype.Worker.prototype.end = function(data){
    if(this.finished){
        return;
    }
    this.finished = true;
    this.gearman.sendCommand("WORK_COMPLETE", this.handle, data);
    delete this.gearman.currentWorkers[this.handle];
    this.gearman.sendCommand("GRAB_JOB");
};

Gearman.prototype.Worker.prototype.error = function(error){
    if(this.finished){
        return;
    }
    this.finished = true;
    this.gearman.sendCommand("WORK_FAIL", this.handle);
    delete this.gearman.currentWorkers[this.handle];
    this.gearman.sendCommand("GRAB_JOB");
};

// JOB

Gearman.prototype.Job = function(gearman, name, payload){
    EventEmitter.call(this);

    this.gearman = gearman;
    this.name = name;
    this.payload = payload;

    gearman.sendCommand("SUBMIT_JOB", name, false, payload, this.receiveHandle.bind(this));
};
utillib.inherits(Gearman.prototype.Job, EventEmitter);

Gearman.prototype.Job.prototype.receiveHandle = function(handle){
    if(handle){
        this.handle = handle;
        this.gearman.currentJobs[handle] = this;
    }else{
        callback(new Error("Invalid response from server"));
    }
};
<commit_msg>completion bug<commit_after>var netlib = require("net"),
    EventEmitter = require('events').EventEmitter,
    utillib = require("util");

module.exports = Gearman;

function Gearman(host, port, debug){
    EventEmitter.call(this);

    this.debug = !!debug;
    this.port = port || 4730;
    this.host = host || "localhost";

    this.connected = this.connecting = this.processing = false;

    this.processing = false;
    this.commandQueue = [];
    this.handleCallbackQueue = [];

    this.remainder = false;

    this.currentJobs = {};
    this.currentWorkers = {};

    this.workers = {};
}
utillib.inherits(Gearman, EventEmitter);

Gearman.packetTypes = {
    CAN_DO: 1,
    CANT_DO: 2,
    RESET_ABILITIES: 3,
    PRE_SLEEP: 4,
    NOOP: 6,
    SUBMIT_JOB: 7,
    JOB_CREATED: 8,
    GRAB_JOB: 9,
    NO_JOB: 10,
    JOB_ASSIGN: 11,
    WORK_STATUS: 12,
    WORK_COMPLETE: 13,
    WORK_FAIL: 14,
    GET_STATUS: 15,
    ECHO_REQ: 16,
    ECHO_RES: 17,
    SUBMIT_JOB_BG: 18,
    ERROR: 19,
    STATUS_RES: 20,
    SUBMIT_JOB_HIGH: 21,
    SET_CLIENT_ID: 22,
    CAN_DO_TIMEOUT: 23,
    ALL_YOURS: 24,
    WORK_EXCEPTION: 25,
    OPTION_REQ: 26,
    OPTION_RES: 27,
    WORK_DATA: 28,
    WORK_WARNING: 29,
    GRAB_JOB_UNIQ: 30,
    JOB_ASSIGN_UNIQ: 31,
    SUBMIT_JOB_HIGH_BG: 32,
    SUBMIT_JOB_LOW: 33,
    SUBMIT_JOB_LOW_BG: 34,
    SUBMIT_JOB_SCHED: 35,
    SUBMIT_JOB_EPOCH: 36
};

Gearman.packetTypesReversed = {
    "1": "CAN_DO",
    "2": "CANT_DO",
    "3": "RESET_ABILITIES",
    "4": "PRE_SLEEP",
    "6": "NOOP",
    "7": "SUBMIT_JOB",
    "8": "JOB_CREATED",
    "9": "GRAB_JOB",
    "10": "NO_JOB",
    "11": "JOB_ASSIGN",
    "12": "WORK_STATUS",
    "13": "WORK_COMPLETE",
    "14": "WORK_FAIL",
    "15": "GET_STATUS",
    "16": "ECHO_REQ",
    "17": "ECHO_RES",
    "18": "SUBMIT_JOB_BG",
    "19": "ERROR",
    "20": "STATUS_RES",
    "21": "SUBMIT_JOB_HIGH",
    "22": "SET_CLIENT_ID",
    "23": "CAN_DO_TIMEOUT",
    "24": "ALL_YOURS",
    "25": "WORK_EXCEPTION",
    "26": "OPTION_REQ",
    "27": "OPTION_RES",
    "28": "WORK_DATA",
    "29": "WORK_WARNING",
    "30": "GRAB_JOB_UNIQ",
    "31": "JOB_ASSIGN_UNIQ",
    "32": "SUBMIT_JOB_HIGH_BG",
    "33": "SUBMIT_JOB_LOW",
    "34": "SUBMIT_JOB_LOW_BG",
    "35": "SUBMIT_JOB_SCHED",
    "36": "SUBMIT_JOB_EPOCH"
};

Gearman.paramCount = {
    ERROR:          ["string", "string"],
    JOB_ASSIGN:     ["string", "string", "buffer"],
    JOB_ASSIGN_UNIQ:["string", "string", "string", "buffer"],
    JOB_CREATED:    ["string"],
    WORK_COMPLETE:  ["string", "buffer"],
    WORK_EXCEPTION: ["string", "string"],
    WORK_WARNING:   ["string", "string"],
    WORK_DATA:      ["string", "buffer"],
    WORK_FAIL:      ["string"],
    WORK_STATUS:    ["string", "number", "number"]
};

Gearman.prototype.connect = function(){

    if(this.connected || this.connecting){
        // juhul kui ühendus on juba olemas käivita protsessimine
        if(this.connected && !this.processing){
            this.processCommandQueue();
        }
        return false;
    }

    this.connecting = true;

    if(this.debug){
        console.log("connecting...");
    }

    this.socket = netlib.createConnection(this.port, this.host);

    this.socket.on("connect", (function(){

        this.connecting = false;
        this.connected = true;

        if(this.debug){
            console.log("connected!");
        }
        this.emit("connect");

        this.processCommandQueue();

    }).bind(this));

    this.socket.on("end", this.close.bind(this));
    this.socket.on("close", this.close.bind(this));
    this.socket.on("timeout", this.close.bind(this));

    this.socket.on("error", this.errorHandler.bind(this));

    this.socket.on("data", this.receive.bind(this));
};

Gearman.prototype.close = function(){
    if(this.connected){
        this.closeConnection();
        this.emit("close");
    }
};

Gearman.prototype.closeConnection = function(){
    var i;

    if(this.connected){
        if(this.socket){
            try{
                this.socket.end();
            }catch(E){}
        }
        this.connected = false;
        this.connecting = false;

        // clear current jobs
        for(i in this.currentJobs){
            if(this.currentJobs.hasOwnProperty(i)){
                if(this.currentJobs[i]){
                    this.currentJobs[i].emit("error", new Error("Job failed"));
                }
                delete this.currentJobs[i];
            }
        }

        // clear current workers
        for(i in this.currentWorkers){
            if(this.currentWorkers.hasOwnProperty(i)){
                delete this.currentWorkers[i];
            }
        }
    }
};

Gearman.prototype.errorHandler = function(err){
    this.emit("error", err);
    this.closeConnection();
};

Gearman.prototype.processCommandQueue = function(chunk){
    var command;
    if(this.commandQueue.length){
        this.processing = true;
        command = this.commandQueue.shift();
        this.sendCommandToServer.apply(this, command);
    }else{
        this.processing = false;
    }
};

Gearman.prototype.sendCommand = function(){
    var command = Array.prototype.slice.call(arguments);
    this.commandQueue.push(command);
    if(!this.processing){
        this.processCommandQueue();
    }
};

Gearman.prototype.sendCommandToServer = function(){
    var body,
        args = Array.prototype.slice.call(arguments),
        commandName, commandId, commandCallback,
        i, len, bodyLength = 0, curpos = 12;

    if(!this.connected){
        this.commandQueue.unshift(args);
        return this.connect();
    }

    commandName = (args.shift() || "").trim().toUpperCase();

    if(args.length && typeof args[args.length-1] == "function"){
        commandCallback = args.pop();
        this.handleCallbackQueue.push(commandCallback);
    }

    commandId = Gearman.packetTypes[commandName] || 0;

    if(!commandId){
        // TODO: INVALID COMMAND!
    }

    for(i=0, len=args.length; i<len; i++){
        if(!(args[i] instanceof Buffer)){
            args[i] = new Buffer((args[i] || "").toString(), "utf-8");
        }
        bodyLength += args[i].length;
    }

    bodyLength += args.length>1 ? args.length - 1 : 0;

    body = new Buffer(bodyLength + 12); // packet size + 12 byte header

    body.writeUInt32BE(0x00524551, 0); // \0REQ
    body.writeUInt32BE(commandId, 4); // \0REQ
    body.writeUInt32BE(bodyLength, 8); // \0REQ

    // compose body
    for(i=0, len = args.length; i<len; i++){
        args[i].copy(body, curpos);
        curpos += args[i].length;
        if(i < args.length-1){
            body[curpos++] = 0x00;
        }
    }

    if(this.debug){
        console.log("Sending: " + commandName + " with "+args.length+" params");
        console.log(" - ", body);
        args.forEach(function(arg, i){
            console.log("  - ARG:"+i+" ", arg.toString());
        });
    }

    this.socket.write(body, this.processCommandQueue.bind(this));
};

Gearman.prototype.receive = function(chunk){
    var data = new Buffer((chunk && chunk.length || 0) + (this.remainder && this.remainder.length || 0)),
        commandId, commandName,
        bodyLength = 0, args = [], argTypes, curarg, i, len, argpos, curpos;

    // nothing to do here
    if(!data.length){
        return;
    }

    // if theres a remainder value, tie it together with the incoming chunk
    if(this.remainder){
        this.remainder.copy(data, 0, 0);
        if(chunk){
            chunk.copy(data, this.remainder.length, 0);
        }
    }else{
        data = chunk;
    }

    // response header needs to be at least 12 bytes
    // otherwise keep the current chunk as remainder
    if(data.length<12){
        this.remainder = data;
        return;
    }

    if(data.readUInt32BE(0) != 0x00524553){
        // OUT OF SYNC!
        return this.errorHandler(new Error("Out of sync with server"));
    }

    // response needs to be 12 bytes + payload length
    bodyLength = data.readUInt32BE(8);
    if(data.length < 12 + bodyLength){
        this.remainder = data;
        return;
    }

    // keep the remainder if incoming data is larger than needed
    if(data.length > 12 + bodyLength){
        this.remainder = data.slice(12 + bodyLength);
        data = data.slice(0, 12 + bodyLength);
    }else{
        this.remainder = false;
    }

    commandId = data.readUInt32BE(4);
    commandName = (Gearman.packetTypesReversed[commandId] || "");
    if(!commandName){
        // TODO: UNKNOWN COMMAND!
        return;
    }

    if(bodyLength && (argTypes = Gearman.paramCount[commandName])){
        curpos = 12;
        argpos = 12;

        for(i = 0, len = argTypes.length; i < len; i++){

            if(i < len - 1){
                while(data[curpos] !== 0x00 && curpos < data.length){
                    curpos++;
                }
                curarg = data.slice(argpos, curpos);
            }else{
                curarg = data.slice(argpos);
            }

            switch(argTypes[i]){
                case "string":
                    curarg = curarg.toString("utf-8");
                    break;
                case "number":
                    curarg = Number(curarg.toString()) || 0;
                    break;
            }

            args.push(curarg);

            curpos++;
            argpos = curpos;
            if(curpos >= data.length){
                break;
            }
        }
    }

    if(this.debug){
        console.log("Received: " + commandName + " with " + args.length + " params");
        console.log(" - ", data);
        args.forEach(function(arg, i){
            console.log("  - ARG:"+i+" ", arg.toString());
        });
    }

    // Run command
    if(typeof this["receive_" + commandName] == "function"){
        if(commandName == "JOB_CREATED" && this.handleCallbackQueue.length){
            args = args.concat(this.handleCallbackQueue.shift());
        }
        this["receive_" + commandName].apply(this, args);
    }

    // rerun receive just in case there's enough data for another command
    if(this.remainder && this.remainder.length>=12){
        process.nextTick(this.receive.bind(this));
    }
};

Gearman.prototype.receive_NO_JOB = function(){
    this.sendCommand("PRE_SLEEP");
    this.emit("idle");
};

Gearman.prototype.receive_NOOP = function(){
    this.sendCommand("GRAB_JOB");
};

Gearman.prototype.receive_ECHO_REQ = function(payload){
    this.sendCommand("ECHO_RES", payload);
};

Gearman.prototype.receive_ERROR = function(code, message){
    if(debug){
        console.log("Server error: ", code, message);
    }
};

Gearman.prototype.receive_JOB_CREATED = function(handle, callback){
    if(typeof callback == "function"){
        callback(handle);
    }
};

Gearman.prototype.receive_WORK_FAIL = function(handle){
    var job;
    if(job = this.currentJobs[handle]){
        delete this.currentJobs[handle];
        job.emit("error", new Error("Job failed"));
    }
};

Gearman.prototype.receive_WORK_DATA = function(handle, payload){
    if(this.currentJobs[handle]){
        this.currentJobs[handle].emit("data", payload);
    }
};

Gearman.prototype.receive_WORK_COMPLETE = function(handle, payload){
    var job;
    if(job = this.currentJobs[handle]){
        if(payload){
            job.emit("data", payload);
        }
        delete this.currentJobs[handle];
        job.emit("end");
    }
};

Gearman.prototype.receive_JOB_ASSIGN = function(handle, name, payload){
    if(typeof this.workers[name] == "function"){
        var worker = new this.Worker(this, handle, name, payload);
        this.currentWorkers[handle] = worker;
        this.workers[name](payload, worker);
    }
};

Gearman.prototype.registerWorker = function(name, func){
    if(!this.workers[name]){
        this.sendCommand("CAN_DO", name);
        this.sendCommand("GRAB_JOB");
    }
    this.workers[name] = func;
};

Gearman.prototype.submitJob = function(name, payload){
    return new this.Job(this, name, payload);
};

// WORKER

Gearman.prototype.Worker = function(gearman, handle, name, payload){
    this.gearman = gearman;
    this.handle = handle;
    this.name = name;
    this.payload = payload;
    this.finished = false;
};

Gearman.prototype.Worker.prototype.write = function(data){
    if(this.finished){
        return;
    }
    this.gearman.sendCommand("WORK_DATA", this.handle, data);
};

Gearman.prototype.Worker.prototype.end = function(data){
    if(this.finished){
        return;
    }
    this.finished = true;
    this.gearman.sendCommand("WORK_COMPLETE", this.handle, data);
    delete this.gearman.currentWorkers[this.handle];
    this.gearman.sendCommand("GRAB_JOB");
};

Gearman.prototype.Worker.prototype.error = function(error){
    if(this.finished){
        return;
    }
    this.finished = true;
    this.gearman.sendCommand("WORK_FAIL", this.handle);
    delete this.gearman.currentWorkers[this.handle];
    this.gearman.sendCommand("GRAB_JOB");
};

// JOB

Gearman.prototype.Job = function(gearman, name, payload){
    EventEmitter.call(this);

    this.gearman = gearman;
    this.name = name;
    this.payload = payload;

    gearman.sendCommand("SUBMIT_JOB", name, false, payload, this.receiveHandle.bind(this));
};
utillib.inherits(Gearman.prototype.Job, EventEmitter);

Gearman.prototype.Job.prototype.receiveHandle = function(handle){
    if(handle){
        this.handle = handle;
        this.gearman.currentJobs[handle] = this;
    }else{
        callback(new Error("Invalid response from server"));
    }
};
<|endoftext|>


<commit_before>/* This prototype allows you to sum the numbers in an array. Once an array has been summed, a new
 * member of the array called "total" will be added to the array. It's smart enough to convert
 * strings in quotes into JavaScript number types if the strings are numbers.
 *
 * USAGE EXAMPLE:
 *
 * var myNum = [3, "4.4", 10];
 * myNum.sum();
 * console.log(myNum.total);                                // Returns 17.4;
 *
 * var mySecondNum = [0.002312, 2000.1, 0.2]
 * mySecondNum.sum();
 * console.log(mySecondNum.total);                          // Returns 2000.302312
 */

Array.prototype.sum = function() {
	var k = Object.keys(this);
	var total = 0;

	var decimals = 0;

	// First we replace strings with numbers and get the longest decimal length
	for (var i = 0; i < this.length; i++) {
		if(k[i] !== "total") { // Avoid including a "total" if one already exists.
			if (typeof this[i] === "number") {
				var decLength = (this[i].toString().split('.')[1] || []).length;
				if(decLength > decimals) {
					decimals = decLength;
				}
			} else if (typeof this[i] === "string") {
				var retry = +this[i];
				if (typeof retry === "number" && retry !== "NaN") {
					var decLength = (retry.toString().split('.')[1] || []).length;
					if(decLength > decimals) {
						decimals = decLength;
					}
					this[i] = retry;
				} else {
					console.log("String '" + this[i] + "' in place #" + i + " is not a number and can't be converted to one.");
					return;
				}
			} else {
				console.log("Object in place #" + i + " is not a number or string.");
				return;
			}
		}
	}

	// Now we set a multiplier so we're summing integers, since JavaScript doesn't deal well with decimals.
	if(decimals !== 0) {
		var multiplier = Math.pow(10, decimals);
	} else {
		var multiplier = 1; // Default if none of the numbers are decimals.
	}

	// Next we multiply everything by the multiplier and add it to the total
	for (var i = 0; i < this.length; i++) {
		if(k[i] !== "total") { // Again, avoid including a "total" if one already exists.
			var entry = this[i] * multiplier;
			total += entry
		}
	}

	// Finally, we divide the total by the multiplier and pass the value to the array.
    this["total"] = total/multiplier;
}
<commit_msg>Better logic for identifying NaN.<commit_after>/* This prototype allows you to sum the numbers in an array. Once an array has been summed, a new
 * member of the array called "total" will be added to the array. It's smart enough to convert
 * strings in quotes into JavaScript number types if the strings are numbers.
 *
 * USAGE EXAMPLE:
 *
 * var myNum = [3, "4.4", 10];
 * myNum.sum();
 * console.log(myNum.total);                                // Returns 17.4;
 *
 * var mySecondNum = [0.002312, 2000.1, 0.2]
 * mySecondNum.sum();
 * console.log(mySecondNum.total);                          // Returns 2000.302312
 */

Array.prototype.sum = function() {
	var k = Object.keys(this);
	var total = 0;

	var decimals = 0;

	// First we replace strings with numbers and get the longest decimal length
	for (var i = 0; i < this.length; i++) {
		if(k[i] !== "total") { // Avoid including a "total" if one already exists.
			if (typeof this[i] === "number") {
				var decLength = (this[i].toString().split('.')[1] || []).length;
				if(decLength > decimals) {
					decimals = decLength;
				}
			} else if (typeof this[i] === "string") {
				var retry = +this[i];
				if(!isNaN(retry)) {
				//if(typeof retry === "number" && retry !== NaN) {
					var decLength = (retry.toString().split('.')[1] || []).length;
					if(decLength > decimals) {
						decimals = decLength;
					}
					this[i] = retry;
				} else {
					console.log("String '" + this[i] + "' in place #" + i + " is not a number and can't be converted to one.");
					return;
				}
			} else {
				console.log("Object in place #" + i + " is not a number or string.");
				return;
			}
		}
	}

	// Now we set a multiplier so we're summing integers, since JavaScript doesn't deal well with decimals.
	if(decimals !== 0) {
		var multiplier = Math.pow(10, decimals);
	} else {
		var multiplier = 1; // Default if none of the numbers are decimals.
	}

	// Next we multiply everything by the multiplier and add it to the total
	for (var i = 0; i < this.length; i++) {
		if(k[i] !== "total") { // Again, avoid including a "total" if one already exists.
			var entry = this[i] * multiplier;
			total += entry
		}
	}

	// Finally, we divide the total by the multiplier and pass the value to the array.
    this["total"] = total/multiplier;
}
<|endoftext|>


<commit_before>"use strict";

var sira = require('sira');
var assert = require('chai').assert;

var _describe = {};
var _it = {};
var _beforeEach = {};
var helpers = exports = module.exports = {
    describe: _describe,
    it: _it,
    beforeEach: _beforeEach
};

function mixin(obj, into) {
    Object.keys(obj).forEach(function (key) {
        if (typeof obj[key] === 'function') {
            into[key] = obj[key];
        }
    });
}

_beforeEach.withSapp = function (sapp) {
    if (sapp.models.User) {
        // Speed up the password hashing algorithm
        sapp.models.User.settings.saltWorkFactor = 4;
    }

    beforeEach(function () {
        var test = this;
        test.sapp = sapp;
    });
};

_describe.staticMethod = function (methodName, cb) {
    describe('.' + methodName, function () {
        beforeEach(function () {
            var test = this;
            test.method = methodName;
            test.isStaticMethod = true;
        });
        cb();
    });
};

_describe.instanceMethod = function (methodName, cb) {
    describe('.prototype.' + methodName, function () {
        beforeEach(function () {
            var test = this;
            test.method = methodName;
            test.isInstanceMethod = true;
        });
        cb();
    });
};

_beforeEach.withArgs = function () {
    var args = Array.prototype.slice.call(arguments, 0);
    beforeEach(function () {
        var test = this;
        test.args = args;
    });
};

_beforeEach.givenModel = function (modelName, attrs, optionalHandler) {
    var modelKey = modelName;

    if (typeof attrs === 'function') {
        optionalHandler = attrs;
        attrs = undefined;
    }

    if (typeof optionalHandler === 'string') {
        modelKey = optionalHandler;
    }

    attrs = attrs || {};

    beforeEach(function (done) {
        var test = this;
        var sapp = test.sapp;
        var model = sapp.model(modelName);
        assert(model, 'cannot get model of name ' + modelName + ' from app.models');
        assert(model.schema, 'cannot test model ' + modelName + ' without schema');
        assert(typeof model.create === 'function', modelName + ' does not have a create method');

        model.create(attrs, function (err, result) {
            if (err) {
                console.error(err.message);
                if (err.details) console.error(err.details);
            } else {
                test[modelKey] = result;
            }
            done(err);
        });
    });

    if (typeof optionalHandler === 'function') {
        beforeEach(optionalHandler);
    }

    afterEach(function (done) {
        this[modelKey].destroy(done);
    });
};

_beforeEach.givenUser = function (attrs, optionalHandler) {
    _beforeEach.givenModel('user', attrs, optionalHandler);
};

_beforeEach.givenLoggedInUser = function (credentials, optionalHandler) {
    _beforeEach.givenUser(credentials, function (done) {
        var test = this;
        test.user.constructor.login(credentials, function (err, token) {
            if (err) return done(err);
            test.loggedInAccessToken = token;
            done();
        });
    });

    afterEach(function (done) {
        var test = this;
        test.loggedInAccessToken.destroy(function (err) {
            if (err) return done(err);
            test.loggedInAccessToken = undefined;
            done();
        });
    });
};

_beforeEach.givenAnUnauthenticatedToken = function (attrs, optionalHandler) {
    _beforeEach.givenModel('accessToken', attrs, optionalHandler);
};

_beforeEach.givenAnAnonymousToken = function (attrs, optionalHandler) {
    _beforeEach.givenModel('accessToken', {id: '$anonymous'}, optionalHandler);
};

_describe.whenCalledLocally = function (uri, data, cb) {
    if (cb == undefined) {
        cb = data;
        data = null;
    }

    var uriStr = uri;
    if (typeof uri === 'function') {
        uriStr = '/<dynamic>';
    }

    describe('handle ' + uriStr, function () {
        beforeEach(function (cb) {
            if (typeof uri === 'function') {
                uri = uri.call(this);
            }
            var test = this;
            test.uri = test.uri || uri;

            var payload = data || {};
            if (typeof data === 'function') {
                payload = data.call(this);
            }
            var rek = sira.rekuest(uri, payload);

            if (this.loggedInAccessToken) {
                rek.prop('accessToken', test.loggedInAccessToken);
            }

            rek.send(test.sapp, function (err, result) {
                test.err = err;
                test.result = result;
                cb();
            });

        });

        cb();
    });
};

_describe.whenLoggedInAsUser = function (credentials, cb) {
    describe('when logged in as user', function () {
        _beforeEach.givenLoggedInUser(credentials);
        cb();
    });
};

_describe.whenCalledByUser = function (credentials, uri, data, cb) {
    describe('when called by logged in user', function () {
        _beforeEach.givenLoggedInUser(credentials);
        _describe.whenCalledLocally(uri, data, cb);
    });
};

_describe.whenCalledAnonymously = function (uri, data, cb) {
    describe('when called anonymously', function () {
        _beforeEach.givenAnAnonymousToken();
        _describe.whenCalledLocally(uri, data, cb);
    });
};

_describe.whenCalledUnauthenticated = function (uri, data, cb) {
    describe('when called with unauthenticated token', function () {
        _beforeEach.givenAnAnonymousToken();
        _describe.whenCalledLocally(uri, data, cb);
    });
};

_it.shouldBeAllowed = function () {
    it('should be allowed', function () {
        assert(!this.err);
    });
};

_it.shouldBeDenied = function () {
    it('should not be allowed', function () {
        assert(this.err);
        var expectedStatus = this.aclErrorStatus ||
            this.sapp && this.sapp.options['aclErrorStatus'] ||
            401;
        assert.equal(this.err.statusCode, expectedStatus);
    });
};

_it.shouldNotBeFound = function () {
    it('should not be found', function () {
        assert(this.err);
        assert.equal(this.err.statusCode, 404);
    });
};

_it.shouldBeAllowedWhenCalledAnonymously = function (uri, data) {
    _describe.whenCalledAnonymously(uri, data, function () {
        _it.shouldBeAllowed();
    });
};

_it.shouldBeDeniedWhenCalledAnonymously = function (uri) {
    _describe.whenCalledAnonymously(uri, function () {
        _it.shouldBeDenied();
    });
};

_it.shouldBeAllowedWhenCalledUnauthenticated = function (uri, data) {
    _describe.whenCalledUnauthenticated(uri, data, function () {
        _it.shouldBeAllowed();
    });
};

_it.shouldBeDeniedWhenCalledUnauthenticated = function (uri) {
    _describe.whenCalledUnauthenticated(uri, function () {
        _it.shouldBeDenied();
    });
};

_it.shouldBeAllowedWhenCalledByUser = function (credentials, uri, data) {
    _describe.whenCalledByUser(credentials, uri, data, function () {
        _it.shouldBeAllowed();
    });
};

_it.shouldBeDeniedWhenCalledByUser = function (credentials, uri) {
    _describe.whenCalledByUser(credentials, uri, function () {
        _it.shouldBeDenied();
    });
};

<commit_msg>refactor<commit_after>"use strict";

var sira = require('sira');
var assert = require('chai').assert;

var _describe = {};
var _it = {};
var _beforeEach = {};
var helpers = exports = module.exports = {
    describe: _describe,
    it: _it,
    beforeEach: _beforeEach
};

function merge(into, obj) {
    if (!into || !obj) return into;
    Object.keys(obj).forEach(function (key) {
        if (obj.hasOwnProperty(key)) {
            into[key] = obj[key];
        }
    });
}

_beforeEach.withSapp = function (sapp) {
    if (sapp.models.User) {
        // Speed up the password hashing algorithm
        sapp.models.User.settings.saltWorkFactor = 4;
    }

    beforeEach(function () {
        var test = this;
        test.sapp = sapp;
    });
};

_describe.staticMethod = function (methodName, cb) {
    describe('.' + methodName, function () {
        beforeEach(function () {
            var test = this;
            test.method = methodName;
            test.isStaticMethod = true;
        });
        cb();
    });
};

_describe.instanceMethod = function (methodName, cb) {
    describe('.prototype.' + methodName, function () {
        beforeEach(function () {
            var test = this;
            test.method = methodName;
            test.isInstanceMethod = true;
        });
        cb();
    });
};

_beforeEach.withArgs = function () {
    var args = Array.prototype.slice.call(arguments, 0);
    beforeEach(function () {
        var test = this;
        test.args = args;
    });
};

_beforeEach.givenModel = function (modelName, attrs, optionalHandler) {
    var modelKey = modelName;

    if (typeof attrs === 'function') {
        optionalHandler = attrs;
        attrs = undefined;
    }

    if (typeof optionalHandler === 'string') {
        modelKey = optionalHandler;
    }

    attrs = attrs || {};

    beforeEach(function (done) {
        var test = this;
        var sapp = test.sapp;
        var model = sapp.model(modelName);
        assert(model, 'cannot get model of name ' + modelName + ' from app.models');
        assert(model.schema, 'cannot test model ' + modelName + ' without schema');
        assert(typeof model.create === 'function', modelName + ' does not have a create method');

        model.create(attrs, function (err, result) {
            if (err) {
                console.error(err.message);
                if (err.details) console.error(err.details);
            } else {
                test[modelKey] = result;
            }
            done(err);
        });
    });

    if (typeof optionalHandler === 'function') {
        beforeEach(optionalHandler);
    }

    afterEach(function (done) {
        this[modelKey].destroy(done);
    });
};

_beforeEach.givenUser = function (attrs, optionalHandler) {
    _beforeEach.givenModel('user', attrs, optionalHandler);
};

_beforeEach.givenLoggedInUser = function (credentials, optionalHandler) {
    _beforeEach.givenUser(credentials, function (done) {
        var test = this;
        test.user.constructor.login(credentials, function (err, token) {
            if (err) return done(err);
            test.loggedInAccessToken = token;
            done();
        });
    });

    afterEach(function (done) {
        var test = this;
        test.loggedInAccessToken.destroy(function (err) {
            if (err) return done(err);
            test.loggedInAccessToken = undefined;
            done();
        });
    });
};

_beforeEach.givenAnUnauthenticatedToken = function (attrs, optionalHandler) {
    _beforeEach.givenModel('accessToken', attrs, optionalHandler);
};

_beforeEach.givenAnAnonymousToken = function (attrs, optionalHandler) {
    _beforeEach.givenModel('accessToken', {id: '$anonymous'}, optionalHandler);
};

_describe.whenCalledLocally = function (uri, data, cb) {
    if (cb == undefined) {
        cb = data;
        data = null;
    }

    var uriStr = uri;
    if (typeof uri === 'function') {
        uriStr = '/<dynamic>';
    }

    describe('handle ' + uriStr, function () {
        beforeEach(function (cb) {
            if (typeof uri === 'function') {
                uri = uri.call(this);
            }
            if (typeof uri === 'object') {
                data = merge(data || {}, uri.data);
                uri = uri.uri;
            }
            var test = this;
            test.uri = test.uri || uri;

            var payload = data || {};
            if (typeof data === 'function') {
                payload = data.call(this);
            }
            var rek = sira.rekuest(uri, payload);

            if (this.loggedInAccessToken) {
                rek.prop('accessToken', test.loggedInAccessToken);
            }

            rek.send(test.sapp, function (err, result) {
                test.err = err;
                test.result = result;
                cb();
            });

        });

        cb();
    });
};

_describe.whenLoggedInAsUser = function (credentials, cb) {
    describe('when logged in as user', function () {
        _beforeEach.givenLoggedInUser(credentials);
        cb();
    });
};

_describe.whenCalledByUser = function (credentials, uri, data, cb) {
    describe('when called by logged in user', function () {
        _beforeEach.givenLoggedInUser(credentials);
        _describe.whenCalledLocally(uri, data, cb);
    });
};

_describe.whenCalledAnonymously = function (uri, data, cb) {
    describe('when called anonymously', function () {
        _beforeEach.givenAnAnonymousToken();
        _describe.whenCalledLocally(uri, data, cb);
    });
};

_describe.whenCalledUnauthenticated = function (uri, data, cb) {
    describe('when called with unauthenticated token', function () {
        _beforeEach.givenAnAnonymousToken();
        _describe.whenCalledLocally(uri, data, cb);
    });
};

_it.shouldBeAllowed = function () {
    it('should be allowed', function () {
        assert(!this.err);
    });
};

_it.shouldBeDenied = function () {
    it('should not be allowed', function () {
        assert(this.err);
        var expectedStatus = this.aclErrorStatus ||
            this.sapp && this.sapp.options['aclErrorStatus'] ||
            401;
        assert.equal(this.err.statusCode, expectedStatus);
    });
};

_it.shouldNotBeFound = function () {
    it('should not be found', function () {
        assert(this.err);
        assert.equal(this.err.statusCode, 404);
    });
};

_it.shouldBeAllowedWhenCalledAnonymously = function (uri, data) {
    _describe.whenCalledAnonymously(uri, data, function () {
        _it.shouldBeAllowed();
    });
};

_it.shouldBeDeniedWhenCalledAnonymously = function (uri) {
    _describe.whenCalledAnonymously(uri, function () {
        _it.shouldBeDenied();
    });
};

_it.shouldBeAllowedWhenCalledUnauthenticated = function (uri, data) {
    _describe.whenCalledUnauthenticated(uri, data, function () {
        _it.shouldBeAllowed();
    });
};

_it.shouldBeDeniedWhenCalledUnauthenticated = function (uri) {
    _describe.whenCalledUnauthenticated(uri, function () {
        _it.shouldBeDenied();
    });
};

_it.shouldBeAllowedWhenCalledByUser = function (credentials, uri, data) {
    _describe.whenCalledByUser(credentials, uri, data, function () {
        _it.shouldBeAllowed();
    });
};

_it.shouldBeDeniedWhenCalledByUser = function (credentials, uri) {
    _describe.whenCalledByUser(credentials, uri, function () {
        _it.shouldBeDenied();
    });
};

<|endoftext|>


<commit_before>/**
 * Created by JetBrains WebStorm.
 * User: brox
 * Date: 31/01/12
 * Time: 18:59
 * To change this template use File | Settings | File Templates.
 */
var redis = require('redis');
var config = require('./config_base').queue;
var logger = require('./logger.js');

// ?????? Pool grande de conexiones a redis? Tiene sentido???

var rcli = redis.createClient(redis.DEFAULT_PORT, config.redis_host);
var rcliBlocking = redis.createClient(redis.DEFAULT_PORT, config.redis_host);

//redis.debug_mode = true;

function put(key, obj, err_fun) {

    logger.info('in  put');

    var simple_req_str = JSON.stringify(obj);

    logger.info('simple_req_str: '+ simple_req_str);

    rcli.lpush(key, simple_req_str,err_fun);
}

function get(keys, aux_queue_id, callback) {

    logger.info('keys', keys);

    rcliBlocking.brpop(keys.control, keys.hpri, keys.lpri , 0, function onPop(err, data) {
            logger.info("data",data);
            //technical DEBT dou to REDIS unsupported functionality
            //BRPOPLPUSH from multiple sources OR LUA Scripting
            rcli.lpush(aux_queue_id, data, function onPush(err){
                var obj = JSON.parse(data[1]);
                callback(err, { queueId: data[0], task: obj });
            });

    });

}

function get_pending(idconsumer, callback){
    logger.info('Getting pending elem from: '+idconsumer);
    rcli.rpop(idconsumer, function onPendingData(err, data){
       if(callback){callback(err, data);}
    });
}

function rem_processing_queue(idconsumer, callback) {
    logger.info('removing aux elem from: '+idconsumer);
    rcli.del(idconsumer, callback);
}

exports.put = put;
exports.get = get;
exports.rem_processing_queue = rem_processing_queue;
exports.get_pending = get_pending;
<commit_msg>fixing returned pending data<commit_after>/**
 * Created by JetBrains WebStorm.
 * User: brox
 * Date: 31/01/12
 * Time: 18:59
 * To change this template use File | Settings | File Templates.
 */
var redis = require('redis');
var config = require('./config_base').queue;
var logger = require('./logger.js');

// ?????? Pool grande de conexiones a redis? Tiene sentido???

var rcli = redis.createClient(redis.DEFAULT_PORT, config.redis_host);
var rcliBlocking = redis.createClient(redis.DEFAULT_PORT, config.redis_host);

//redis.debug_mode = true;

function put(key, obj, err_fun) {

    logger.info('in  put');

    var simple_req_str = JSON.stringify(obj);

    logger.info('simple_req_str: '+ simple_req_str);

    rcli.lpush(key, simple_req_str,err_fun);
}

function get(keys, aux_queue_id, callback) {

    logger.info('keys', keys);

    rcliBlocking.brpop(keys.control, keys.hpri, keys.lpri , 0, function onPop(err, data) {
            logger.info("data",data);
            //technical DEBT dou to REDIS unsupported functionality
            //BRPOPLPUSH from multiple sources OR LUA Scripting
            rcli.lpush(aux_queue_id, data, function onPush(err){
                var obj = JSON.parse(data[1]);
                callback(err, { queueId: data[0], task: obj });
            });

    });

}

function get_pending(idconsumer, callback){
    logger.info('Getting pending elem from: '+idconsumer);
    rcli.rpop(idconsumer, function onPendingData(err, data){
        var obj = JSON.parse(data[1]);
        if(callback){callback(err, { queueId: data[0], task: obj });}
    });
}

function rem_processing_queue(idconsumer, callback) {
    logger.info('removing aux elem from: '+idconsumer);
    rcli.del(idconsumer, callback);
}

exports.put = put;
exports.get = get;
exports.rem_processing_queue = rem_processing_queue;
exports.get_pending = get_pending;
<|endoftext|>