<commit_before>var Promise = require('promise');

function identity(x) {
    return x;
}

function argsOrArr(func) {
    return function(arr) {
        if (arguments.length > 1) {
            arr = Array.prototype.slice.call(arguments, 0);
        } else if (!(arr instanceof Array)) {
            arr = [arr];
        }
        return func.call(this, arr);
    };
}

exports.getResolved = function(val) {
    return new Promise(function(res) {
        res(val);
    });
};
exports.getRejected = function(val) {
    return new Promise(function(_, rej) {
        rej(val);
    });
};

exports.all = Promise.all.bind(Promise);

exports.first = argsOrArr(function(arr) {
    return new Promise(function(resolve, reject) {
        for (var i = 0; i < arr.length; i++) {
            arr[i].then(resolve, reject);
        }
    });
});

exports.any = argsOrArr(function(arr) {
    var len = arr.length;
    var rejected = 0;
    return new Promise(function(resolve, reject) {
        for (var i = 0; i < len; i++) {
            arr[i].then(resolve, function() {
                rejected++;
                if (rejected === len) {
                    reject();
                }
            });
        }
    });
});

exports.rateLimit = function(limit, arr, rejectShouldAbort) {
    var active = 0;
    var index = 0;
    // Keep a copy of the input so we don't modify the original.
    var queue = arr.slice(0);

    var results = [];

    return new Promise(function(resolve, reject) {
        function runOne() {
            active++;
            var idx = index++;
            var current = queue.shift()();

            current = current.then(function(val) {
                active--;
                results[idx] = val;
                if (queue.length) {
                    setImmediate(runOne);
                } else if (active === 0) {
                    resolve(results);
                }
            }, function() {
                active--;
                if (rejectShouldAbort) {
                    reject();
                } else if (queue.length) {
                    setImmediate(runOne);
                } else if (active === 0) {
                    resolve(results);
                }
            });
        }

        for (var i = 0; i < limit && queue.length; i++) {
            runOne();
        }

    });
};

exports.map = function(arr, iterator, context) {
    var len = arr.length;
    var resolved = 0;

    if (context) iterator = iterator.bind(context);

    var results = [];
    return new Promise(function(resolve, reject) {
        arr.forEach(function(prom, i) {
            prom.then(iterator).then(function(val) {
                results[i] = val;
                if (++resolved === len) {
                    resolve(results);
                }
            }, reject);
        });
    });
};

exports.filter = function(arr, iterator, context) {
    var len = arr.length;
    var completed = 0;

    iterator = iterator || identity;
    if (context) iterator = iterator.bind(context);

    var resMap = [];
    var results = [];
    return new Promise(function(resolve, reject) {
        arr.forEach(function(prom, i) {
            prom = prom.then(function(val) {
                return results[i] = val;
            }).then(iterator).then(function(val) {
                if (val === false) return;
                resMap[i] = true;
            });
            _always(prom).then(function() {
                if (++completed !== len) return;
                resolve(results.filter(function(_, i) {
                    return resMap[i];
                }));
            });
        });
    });
};

exports.ignore = function(arr) {
    var len = arr.length;
    var completed = 0;

    var resMap = [];
    var results = [];
    return new Promise(function(resolve, reject) {
        arr.forEach(function(prom, i) {
            prom = prom.then(function(val) {
                resMap[i] = true;
                results[i] = val;
            });
            _always(prom).then(function() {
                if (++completed !== len) return;
                resolve(results.filter(function(_, i) {
                    return resMap[i];
                }));
            });
        });
    });
};

var _always = exports.always = function(promise) {
    return new Promise(function(res) {
        promise.then(res, res);
    });
}
<commit_msg>Alias first() to race()<commit_after>var Promise = require('promise');

function identity(x) {
    return x;
}

function argsOrArr(func) {
    return function(arr) {
        if (arguments.length > 1) {
            arr = Array.prototype.slice.call(arguments, 0);
        } else if (!(arr instanceof Array)) {
            arr = [arr];
        }
        return func.call(this, arr);
    };
}

exports.getResolved = function(val) {
    return new Promise(function(res) {
        res(val);
    });
};
exports.getRejected = function(val) {
    return new Promise(function(_, rej) {
        rej(val);
    });
};

exports.all = Promise.all.bind(Promise);

exports.first = exports.race = argsOrArr(function(arr) {
    return new Promise(function(resolve, reject) {
        for (var i = 0; i < arr.length; i++) {
            arr[i].then(resolve, reject);
        }
    });
});

exports.any = argsOrArr(function(arr) {
    var len = arr.length;
    var rejected = 0;
    return new Promise(function(resolve, reject) {
        for (var i = 0; i < len; i++) {
            arr[i].then(resolve, function() {
                rejected++;
                if (rejected === len) {
                    reject();
                }
            });
        }
    });
});

exports.rateLimit = function(limit, arr, rejectShouldAbort) {
    var active = 0;
    var index = 0;
    // Keep a copy of the input so we don't modify the original.
    var queue = arr.slice(0);

    var results = [];

    return new Promise(function(resolve, reject) {
        function runOne() {
            active++;
            var idx = index++;
            var current = queue.shift()();

            current = current.then(function(val) {
                active--;
                results[idx] = val;
                if (queue.length) {
                    setImmediate(runOne);
                } else if (active === 0) {
                    resolve(results);
                }
            }, function() {
                active--;
                if (rejectShouldAbort) {
                    reject();
                } else if (queue.length) {
                    setImmediate(runOne);
                } else if (active === 0) {
                    resolve(results);
                }
            });
        }

        for (var i = 0; i < limit && queue.length; i++) {
            runOne();
        }

    });
};

exports.map = function(arr, iterator, context) {
    var len = arr.length;
    var resolved = 0;

    if (context) iterator = iterator.bind(context);

    var results = [];
    return new Promise(function(resolve, reject) {
        arr.forEach(function(prom, i) {
            prom.then(iterator).then(function(val) {
                results[i] = val;
                if (++resolved === len) {
                    resolve(results);
                }
            }, reject);
        });
    });
};

exports.filter = function(arr, iterator, context) {
    var len = arr.length;
    var completed = 0;

    iterator = iterator || identity;
    if (context) iterator = iterator.bind(context);

    var resMap = [];
    var results = [];
    return new Promise(function(resolve, reject) {
        arr.forEach(function(prom, i) {
            prom = prom.then(function(val) {
                return results[i] = val;
            }).then(iterator).then(function(val) {
                if (val === false) return;
                resMap[i] = true;
            });
            _always(prom).then(function() {
                if (++completed !== len) return;
                resolve(results.filter(function(_, i) {
                    return resMap[i];
                }));
            });
        });
    });
};

exports.ignore = function(arr) {
    var len = arr.length;
    var completed = 0;

    var resMap = [];
    var results = [];
    return new Promise(function(resolve, reject) {
        arr.forEach(function(prom, i) {
            prom = prom.then(function(val) {
                resMap[i] = true;
                results[i] = val;
            });
            _always(prom).then(function() {
                if (++completed !== len) return;
                resolve(results.filter(function(_, i) {
                    return resMap[i];
                }));
            });
        });
    });
};

var _always = exports.always = function(promise) {
    return new Promise(function(res) {
        promise.then(res, res);
    });
}
<|endoftext|>


<commit_before>const cheerio = require('cheerio');
const request = require('request');
const fs = require('fs');
const _ = require('lodash');

module.exports = save;

/**
 * @param  {string} identifier media ID or URL
 * @return {Promise}
 */
function save(identifier, dir) {
  return new Promise((resolve, reject) => {
    const url = identifier.substring(0, 8) === 'https://' ? identifier : `https://www.instagram.com/p/${identifier}/`;

    request(url, (err, response, body) => {
      if (err) {
        return reject({
          message: `Failed to request resource`,
          url,
          error: err
        });
      }

      const $ = cheerio.load(body);
      const canonicalUrl = $('link[rel="canonical"]').attr('href');

      if (!canonicalUrl) {
        return reject({
          message: `Invalid media ID`,
          url
        });
      }

      const isVideo = $('meta[name="medium"]').attr('content') === 'video';
      const mimeType = isVideo ? $('meta[property="og:video:type"]').attr('content') : 'image/jpeg';
      const mediaId = canonicalUrl.split('/')[4];
      const filename = createFilename(mediaId, mimeType, dir);
      const downloadUrl = isVideo ? $('meta[property="og:video"]').attr('content') : $('meta[property="og:image"]').attr('content');

      request.head(downloadUrl, error => {
        if (error) {
          return reject({
            message: `Download failed`,
            url,
            error
          });
        }

        request(downloadUrl)
          .pipe(fs.createWriteStream(filename))
          .on('close', () => {
            return resolve({
              file: filename,
              type: isVideo ? 'video' : 'photo',
              mimeType,
              url,
              source: downloadUrl
            });
          });
      });
    });
  });
}

/**
 * @param  {string} mediaId
 * @param  {string} mimeType
 * @param  {string} dir
 * @return {string}
 */
function createFilename(mediaId, mimeType, dir) {
  const mimeTypeToExtensionMap = {
    'video/mp4': '.mp4',
    'video/ogg': '.ogg',
    'video/webm': '.webm',
    'image/jpeg': '.jpg'
  };

  const extension = _.get(mimeTypeToExtensionMap, mimeType, '.jpg');

  return `${dir}/${mediaId}${extension}`;
}
<commit_msg>Remove redundant default extension<commit_after>const cheerio = require('cheerio');
const request = require('request');
const fs = require('fs');
const _ = require('lodash');

module.exports = save;

/**
 * @param  {string} identifier media ID or URL
 * @return {Promise}
 */
function save(identifier, dir) {
  return new Promise((resolve, reject) => {
    const url = identifier.substring(0, 8) === 'https://' ? identifier : `https://www.instagram.com/p/${identifier}/`;

    request(url, (err, response, body) => {
      if (err) {
        return reject({
          message: `Failed to request resource`,
          url,
          error: err
        });
      }

      const $ = cheerio.load(body);
      const canonicalUrl = $('link[rel="canonical"]').attr('href');

      if (!canonicalUrl) {
        return reject({
          message: `Invalid media ID`,
          url
        });
      }

      const isVideo = $('meta[name="medium"]').attr('content') === 'video';
      const mimeType = isVideo ? $('meta[property="og:video:type"]').attr('content') : 'image/jpeg';
      const mediaId = canonicalUrl.split('/')[4];
      const filename = createFilename(mediaId, mimeType, dir);
      const downloadUrl = isVideo ? $('meta[property="og:video"]').attr('content') : $('meta[property="og:image"]').attr('content');

      request.head(downloadUrl, error => {
        if (error) {
          return reject({
            message: `Download failed`,
            url,
            error
          });
        }

        request(downloadUrl)
          .pipe(fs.createWriteStream(filename))
          .on('close', () => {
            return resolve({
              file: filename,
              type: isVideo ? 'video' : 'photo',
              mimeType,
              url,
              source: downloadUrl
            });
          });
      });
    });
  });
}

/**
 * @param  {string} mediaId
 * @param  {string} mimeType
 * @param  {string} dir
 * @return {string}
 */
function createFilename(mediaId, mimeType, dir) {
  const mimeTypeToExtensionMap = {
    'video/mp4': '.mp4',
    'video/ogg': '.ogg',
    'video/webm': '.webm',
    'image/jpeg': '.jpg'
  };

  const extension = _.get(mimeTypeToExtensionMap, mimeType);

  return `${dir}/${mediaId}${extension}`;
}
<|endoftext|>


<commit_before>'use strict'
const {resolve} = require('path')
const packages = resolve(__dirname, 'packages')
const {defineProperty} = Object

require('fs')
  .readdirSync(packages)
  .map(item => [resolve(packages, item), item])
  .forEach(([pkgname, item]) => {
    try {
      const desc = {value: require(pkgname), enumerable: true}
      defineProperty(exports, pkgname, desc)
      defineProperty(exports, item, desc)
    } catch (error) {
      console.error(error)
      const desc = {get: () => require(pkgname), enumerable: true}
      defineProperty(exports, pkgname, desc)
      defineProperty(exports, item, desc)
    }
  })
<commit_msg>Refactor<commit_after>'use strict'
const {resolve} = require('path')
const packages = resolve(__dirname, 'packages')
const {defineProperty} = Object

require('fs')
  .readdirSync(packages)
  .map(item => [resolve(packages, item), item])
  .forEach(([pkgname, item]) => {
    const desc = {enumerable: true}

    try {
      desc.value = require(pkgname)
    } catch (error) {
      console.error(error)
      desc.get = () => require(pkgname)
    }

    defineProperty(exports, desc)
  })
<|endoftext|>


<commit_before>module.exports = {
  server: require('./server'),
  client: require('./client'),
  Server: require('./server'), // alias
  Client: require('./client') // alias
}
<commit_msg>update index.js<commit_after>
  exports.server = exports.Server = require('./server');
  exports.client = exports.Client = require('./client');
<|endoftext|>


<commit_before>var _      = require('underscore'),
    ginga  = require('ginga'),
    queue  = require('queue-async'),
    params = ginga.params;

function Wait(parent){
  this._parent = parent;
  this._stack = [];
}
var W = Wait.prototype;
W.parent = function(){
  return this._parent;
};
W.add = function(fn){
  this._stack.push(fn);
  return this;
};
W.invoke = function(){
  _.invoke(this._stack, 'apply', null, arguments);
  this._stack = [];
  return this;
};
W.ended = function(){
  return this._stack.length === 0;
};

module.exports = function( db ){
  var count    = 0,
      queued   = {};

  function Transaction(options){
    this.db = db;
    this.options = options;
    this._id = count;
    count++;

    this._q = queue();
    this.defer = this._q.defer.bind(this._q);

    this._wait = {};
    this._map = {};
    this._deps = {};
    this._batch = [];
  }

  //lock during get, put, del
  function lock(ctx, next, end){
    this.defer(end);
    //options object
    ctx.options = _.defaults({}, ctx.params.opts, this.options);

    //check sublevel
    if(ctx.options && ctx.options.prefix &&
      typeof ctx.options.prefix.sublevel === 'function')
      ctx.prefix = ctx.options.prefix;

    ctx.hash = JSON.stringify(ctx.params.key);
    //key + sublevel prefix hash
    if(ctx.prefix)
      ctx.hash = JSON.stringify([ctx.prefix.prefix(), ctx.params.key]);

    var wait = this._wait[ctx.hash];
    if(wait){
      if(wait.ended()){
        //skip if lock acquired
        next();
      }else{
        //not done waiting -> add to wait list
        wait.add(next);
      }
    }else{
      wait = new Wait(this).add(next);
      var i, j, l;

      if(queued[ctx.hash]){
        //check deadlock and push queue
        for(i = 0, l = queued[ctx.hash].length; i < l; i++){
          var tx = queued[ctx.hash][i].parent();
          if(tx._deps[this._id]){
            wait.invoke(new Error('Deadlock'));
            return;
          }
          this._deps[tx._id] = true;
          for(j in tx._deps)
            this._deps[j] = true;
        }
        queued[ctx.hash].push(wait);
      }else{
        //no queue in hash; lock immediately
        queued[ctx.hash] = [];
        wait.invoke();
      }
      this._wait[ctx.hash] = wait;
    }
  }

  function get(ctx, done){
    if(ctx.hash in this._map){
      done(null, this._map[ctx.hash]);
      return;
    }
    var self = this;

    (ctx.prefix || db).get(ctx.params.key, _.defaults(
      {}, ctx.params.opts, this.options
    ), function(err, val){
      self._map[ctx.hash] = val;
      done(err && !err.notFound ? err:null, val);
    });
  }

  function put(ctx, done){
    this._batch.push(_.defaults({
      type: 'put',
      key: ctx.params.key,
      value: ctx.params.value
    }, ctx.options));

    this._map[ctx.hash] = ctx.params.value;

    done(null);
  }

  function del(ctx, done){
    this._batch.push(_.defaults({
      type: 'del',
      key: ctx.params.key
    }, ctx.options));

    this._map[ctx.hash] = undefined;

    done(null);
  }

  function commit(ctx, next, end){
    var self = this;

    //rollback on commit error
    end(function(err){
      if(err)
        self.rollback();
    });

    this._q.awaitAll(function(err){
      if(err)
        return next(err);
      db.batch(self._batch, function(err, res){
        if(err) next(err);
        else next();
      });
    });
  }

  //release after rollback, commit
  function release(ctx, done){
    var hash, wait;
    for(hash in this._wait){
      wait = this._wait[hash];
      if(wait.ended()){
        if(queued[hash].length > 0){
          queued[hash].shift().invoke();
        }else{
          delete queued[hash];
        }
      }else{
        //clean up waiting jobs
        var idx = queued[hash].indexOf(this._wait[hash]);
        if(idx > -1)
          queued[hash].splice(idx, 1);
      }
      delete this._wait[hash];
    }

    done(null);
  }

  ginga(Transaction.prototype)
    .define('get', params('key','opts?'), lock, get)
    .define('put', params('key','value','opts?'), lock, put)
    .define('del', params('key','opts?'), lock, del)
    .define('rollback', release)
    .define('commit', commit, release);

  db.transaction = db.transaction || function(options){
    return new Transaction(options);
  };
  return db;
};
<commit_msg>progressing<commit_after>var _      = require('underscore'),
    ginga  = require('ginga'),
    queue  = require('queue-async'),
    params = ginga.params;

function Wait(parent){
  this._parent = parent;
  this._stack = [];
}
var W = Wait.prototype;
W.parent = function(){
  return this._parent;
};
W.add = function(fn){
  this._stack.push(fn);
  return this;
};
W.invoke = function(){
  _.invoke(this._stack, 'apply', null, arguments);
  this._stack = [];
  return this;
};
W.ended = function(){
  return this._stack.length === 0;
};

module.exports = function( db ){
  var count    = 0,
      queued   = {};

  function Transaction(options){
    this.db = db;
    this.options = options;
    this._id = count;
    count++;

    this._q = queue();
    this.defer = this._q.defer.bind(this._q);

    this._wait = {};
    this._map = {};
    this._deps = {};
    this._batch = [];
  }

  //lock during get, put, del
  function lock(ctx, next, end){
    //defer commit
    this.defer(function(cb){
      end(function(err){
        cb(err && !err.notFound ? err : null);
      });
    });
    //options object
    ctx.options = _.defaults({}, ctx.params.opts, this.options);

    //check sublevel
    if(ctx.options && ctx.options.prefix &&
      typeof ctx.options.prefix.sublevel === 'function')
      ctx.prefix = ctx.options.prefix;

    ctx.hash = JSON.stringify(ctx.params.key);
    //key + sublevel prefix hash
    if(ctx.prefix)
      ctx.hash = JSON.stringify([ctx.prefix.prefix(), ctx.params.key]);

    var wait = this._wait[ctx.hash];
    if(wait){
      if(wait.ended()){
        //skip if lock acquired
        next();
      }else{
        //not done waiting -> add to wait list
        wait.add(next);
      }
    }else{
      wait = new Wait(this).add(next);
      var i, j, l;

      if(queued[ctx.hash]){
        //check deadlock and push queue
        for(i = 0, l = queued[ctx.hash].length; i < l; i++){
          var tx = queued[ctx.hash][i].parent();
          if(tx._deps[this._id]){
            wait.invoke(new Error('Deadlock'));
            return;
          }
          this._deps[tx._id] = true;
          for(j in tx._deps)
            this._deps[j] = true;
        }
        queued[ctx.hash].push(wait);
      }else{
        //no queue in hash; lock immediately
        queued[ctx.hash] = [];
        wait.invoke();
      }
      this._wait[ctx.hash] = wait;
    }
  }

  function get(ctx, done){
    if(ctx.hash in this._map){
      done(null, this._map[ctx.hash]);
      return;
    }
    var self = this;

    (ctx.prefix || db).get(ctx.params.key, _.defaults(
      {}, ctx.params.opts, this.options
    ), function(err, val){
      self._map[ctx.hash] = val;
      done(err, val);
    });
  }

  function put(ctx, done){
    this._batch.push(_.defaults({
      type: 'put',
      key: ctx.params.key,
      value: ctx.params.value
    }, ctx.options));

    this._map[ctx.hash] = ctx.params.value;

    done(null);
  }

  function del(ctx, done){
    this._batch.push(_.defaults({
      type: 'del',
      key: ctx.params.key
    }, ctx.options));

    this._map[ctx.hash] = undefined;

    done(null);
  }

  function commit(ctx, next, end){
    var self = this;

    //rollback on commit error
    end(function(err){
      if(err)
        self.rollback();
    });

    this._q.awaitAll(function(err){
      if(err)
        return next(err);
      db.batch(self._batch, function(err, res){
        if(err) next(err);
        else next();
      });
    });
  }

  //release after rollback, commit
  function release(ctx, done){
    var hash, wait;
    for(hash in this._wait){
      wait = this._wait[hash];
      if(wait.ended()){
        if(queued[hash].length > 0){
          queued[hash].shift().invoke();
        }else{
          delete queued[hash];
        }
      }else{
        //clean up waiting jobs
        var idx = queued[hash].indexOf(this._wait[hash]);
        if(idx > -1)
          queued[hash].splice(idx, 1);
      }
      delete this._wait[hash];
    }

    done(null);
  }

  ginga(Transaction.prototype)
    .define('get', params('key','opts?'), lock, get)
    .define('put', params('key','value','opts?'), lock, put)
    .define('del', params('key','opts?'), lock, del)
    .define('rollback', release)
    .define('commit', commit, release);

  db.transaction = db.transaction || function(options){
    return new Transaction(options);
  };
  return db;
};
<|endoftext|>


<commit_before>//http://en.wikipedia.org/wiki/Vincenty%27s_formulae
//http://www.movable-type.co.uk/scripts/latlong-vincenty.html#inverse<commit_msg>add degree and radian functions<commit_after>//http://en.wikipedia.org/wiki/Vincenty%27s_formulae
//http://www.movable-type.co.uk/scripts/latlong-vincenty.html#inverse

function toRad(degree) {
 return degree * Math.PI / 180;
};
function toDeg(radian) {
 return radian * 180 / Math.PI;
};<|endoftext|>


<commit_before>var https = require('https');

var options = {
  host: 'www.google.com',
  port: 443,
  path: '/recaptcha/api/siteverify?',
  method: 'GET',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  }
};

module.exports = function(privateKey, remoteIP, response, cb) {
  if( arguments.length < 4 && typeof(response) === 'function') {
    cb = response;
    response = remoteIP;
    remoteIP = undefined;
  }

  var error = null;

  if (!privateKey) {
    error = 'Private key is required';
  } else if (!response) {
    error = 'Response is required';
  }

  if (error) {
    return cb(new Error(error));
  }

  options.path += ('/recaptcha/api/siteverify?secret=' + privateKey + '&response=' + response)
  if (remoteIP) {
    options.path += ('&remoteip=' + remoteIP)
  }

  var request = https.request(options, function(response) {
    var body = '';

    response.on('error', function(err) {
      return cb(new Error(err));
    });

    response.on('data', function(chunk) {
      body += chunk;
    });

    response.on('end', function() {
      var data = JSON.parse( body );
      if (!data.success) return cb(new Error(data['error-codes']));
      cb(null);
    });

  });

  request.on('error', function(err) {
    return cb(new Error(err));
  });

  request.end();

};
<commit_msg>fix(path): overwrite instead of append<commit_after>var https = require('https');

var options = {
  host: 'www.google.com',
  port: 443,
  path: '/recaptcha/api/siteverify?',
  method: 'GET',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  }
};

module.exports = function(privateKey, remoteIP, response, cb) {
  if( arguments.length < 4 && typeof(response) === 'function') {
    cb = response;
    response = remoteIP;
    remoteIP = undefined;
  }

  var error = null;

  if (!privateKey) {
    error = 'Private key is required';
  } else if (!response) {
    error = 'Response is required';
  }

  if (error) {
    return cb(new Error(error));
  }

  options.path = '/recaptcha/api/siteverify?secret=' + privateKey + '&response=' + response;
  if (remoteIP) {
    options.path += '&remoteip=' + remoteIP;
  }

  var request = https.request(options, function(response) {
    var body = '';

    response.on('error', function(err) {
      return cb(new Error(err));
    });

    response.on('data', function(chunk) {
      body += chunk;
    });

    response.on('end', function() {
      var data = JSON.parse( body );
      if (!data.success) return cb(new Error(data['error-codes']));
      cb(null);
    });

  });

  request.on('error', function(err) {
    return cb(new Error(err));
  });

  request.end();

};
<|endoftext|>


<commit_before>function Prober(options) {
    if (!(this instanceof Prober)) {
        return new Prober(options);
    }

    options = options || {};

    this.title = options.title || 'general';
    this.threshold = options.threshold || 3;
    this.window = options.window || 5;
    this.now = options.now || Date.now;
    this.defaultWaitPeriod = options.defaultWaitPeriod || 1000;
    this.maxWaitPeriod = options.maxWaitPeriod || 60000;
    this.enabled = options.enabled || true;
    var detectFailuresBy = options.detectFailuresBy || Prober.detectBy.CALLBACK;
    this.detectFailuresByCallback =
        (detectFailuresBy === Prober.detectBy.CALLBACK) ||
        (detectFailuresBy === Prober.detectBy.BOTH);
    this.detectFailuresByEvent =
        (detectFailuresBy === Prober.detectBy.EVENT) ||
        (detectFailuresBy === Prober.detectBy.BOTH);

    this.failureHandler = options.failureHandler;
    this.logger = options.logger || null;
    this.probes = [];
    this.waitPeriod = this.defaultWaitPeriod;
    this.lastBackendRequest = this.now();
    this.statsd = options.statsd || null;

    if (this.detectFailuresByEvent) {
        if (!options.backend) {
            if (this.logger) {
                this.logger.warn('Prober missing backend from' +
                    ' initialization options');
            }
            return;
        }

        options.backend.on(options.failureEvent, this.notok.bind(this));
        options.backend.on(options.successEvent, this.ok.bind(this));
    }
}

Prober.detectBy = {
    CALLBACK: 'callback',
    EVENT: 'event',
    BOTH: 'both'
};

Prober.prototype.isHealthy = function isHealth() {
    return this.probes.length < this.window ||
        this._getOks().length >= this.threshold;
};

Prober.prototype.isSick = function isSick() {
    return !this.isHealthy();
};

Prober.prototype.notok = function notok() {
    this._addProbe(false);
    if (this.statsd) {
        this.statsd.increment('prober.' + this.title + '.probe.notok');
    }
};

Prober.prototype.notOk = Prober.prototype.notok;

Prober.prototype.ok = function ok() {
    this._addProbe(true);
    if (this.statsd) {
        this.statsd.increment('prober.' + this.title + '.probe.ok');
    }
};

Prober.prototype.setLogger = function setLogger(logger) {
    this.logger = logger;
};

Prober.prototype.probe = function probe(request, bypass, callback) {
    var self = this;

    if (!this.enabled) {
        return request(callback);
    }

    // If the backend is healthy, or it's been enough time
    // that we should check to see if the backend is no longer
    // sick, then make a request to the backend.
    if (this.isHealthy() || this._isPityProbe()) {
        if (this.statsd) {
            this.statsd.increment('prober.' + this.title +
                '.request.performed');
        }

        var wrappedCallback;
        if (this.detectFailuresByCallback) {
            wrappedCallback = function(err, resp) {
                var errResponse = resp && !isNaN(resp.statusCode) &&
                    resp.statusCode >= 500;
                if (err || errResponse) {
                    self.notok();
                } else {
                    self.ok();
                }

                if (callback && typeof callback === 'function') {
                    callback.apply(null, arguments);
                }
            };
        }
        try {
            request(wrappedCallback);
        } catch (e) {
            // we can't log the error here in case the prober is used
            // within the logger. So instead we pass it to the failureHandler
            // the user of this module should not log in the failureHandler
            // maybe send an email instead
            this.failureHandler({
                subject: "Exception in Prober while probing " + this.title,
                body: e.stack
            });
        }

        this.lastBackendRequest = this.now();
    } else {
        if (this.statsd) {
            this.statsd.increment('prober.' + this.title + '.request.bypassed');
        }

        if (bypass && typeof bypass === 'function') {
            bypass(new Error(this.title + ' backend is unhealthy'));
        }
    }
};

Prober.prototype._addProbe = function addProbe(isOk) {
    var timestamp = this.now();
    var logger = this.logger;
    var statsd = this.statsd;

    var wasHealthy = this.isHealthy();
    var thisProbe = { isOk: isOk, timestamp: timestamp };
    this.probes.unshift(thisProbe);
    this.probes = this.probes.slice(0, this.window);
    var isHealthy = this.isHealthy();

    if (wasHealthy && !isHealthy) {
        if (logger) {
            logger.warn(this.title + ' has gotten sick');
        }
        if (statsd) {
            this.statsd.increment('prober.' + this.title + '.health.sick');
        }
    } else if (!wasHealthy && isHealthy) {
        this.waitPeriod = this.defaultWaitPeriod;
        if (logger) {
            logger.warn(this.title + ' has returned to health');
        }
        if (statsd) {
            this.statsd.increment('prober.' + this.title + '.health.recovered');
        }
    } else if (!wasHealthy && !isHealthy) {
        if (statsd) {
            this.statsd.increment('prober.' + this.title +
                '.health.still-sick');
        }

        if (thisProbe.isOk) {
            this.waitPeriod /= 2;
            if (logger) {
                logger.warn(this.title + ' is still sick but last probe was ' +
                    'healthy. Decreased wait period to ' +
                    this.waitPeriod + 'ms');
            }
        } else {
            this.waitPeriod *= 2;

            if (this.waitPeriod > this.maxWaitPeriod) {
                this.waitPeriod = this.maxWaitPeriod;
                if (logger) {
                    logger.warn(this.title + ' is still sick. Wait period is ' +
                        'at its max, ' + this.waitPeriod + 'ms');
                }
            } else if (logger) {
                logger.warn(this.title + ' is still sick. Increased wait ' +
                    'period to ' + this.waitPeriod + 'ms');
            }
        }
    }
};

Prober.prototype._getOks = function _getOks() {
    return this.probes.filter(function(probe) { return probe.isOk; });
};

Prober.prototype._isPityProbe = function _isPityProbe() {
    return this.lastBackendRequest && this.now() >=
        (this.lastBackendRequest + this.waitPeriod);
};

module.exports = Prober;
<commit_msg>handle only one callback<commit_after>function Prober(options) {
    if (!(this instanceof Prober)) {
        return new Prober(options);
    }

    options = options || {};

    this.title = options.title || 'general';
    this.threshold = options.threshold || 3;
    this.window = options.window || 5;
    this.now = options.now || Date.now;
    this.defaultWaitPeriod = options.defaultWaitPeriod || 1000;
    this.maxWaitPeriod = options.maxWaitPeriod || 60000;
    this.enabled = options.enabled || true;
    var detectFailuresBy = options.detectFailuresBy || Prober.detectBy.CALLBACK;
    this.detectFailuresByCallback =
        (detectFailuresBy === Prober.detectBy.CALLBACK) ||
        (detectFailuresBy === Prober.detectBy.BOTH);
    this.detectFailuresByEvent =
        (detectFailuresBy === Prober.detectBy.EVENT) ||
        (detectFailuresBy === Prober.detectBy.BOTH);

    this.failureHandler = options.failureHandler;
    this.logger = options.logger || null;
    this.probes = [];
    this.waitPeriod = this.defaultWaitPeriod;
    this.lastBackendRequest = this.now();
    this.statsd = options.statsd || null;

    if (this.detectFailuresByEvent) {
        if (!options.backend) {
            if (this.logger) {
                this.logger.warn('Prober missing backend from' +
                    ' initialization options');
            }
            return;
        }

        options.backend.on(options.failureEvent, this.notok.bind(this));
        options.backend.on(options.successEvent, this.ok.bind(this));
    }
}

Prober.detectBy = {
    CALLBACK: 'callback',
    EVENT: 'event',
    BOTH: 'both'
};

Prober.prototype.isHealthy = function isHealth() {
    return this.probes.length < this.window ||
        this._getOks().length >= this.threshold;
};

Prober.prototype.isSick = function isSick() {
    return !this.isHealthy();
};

Prober.prototype.notok = function notok() {
    this._addProbe(false);
    if (this.statsd) {
        this.statsd.increment('prober.' + this.title + '.probe.notok');
    }
};

Prober.prototype.notOk = Prober.prototype.notok;

Prober.prototype.ok = function ok() {
    this._addProbe(true);
    if (this.statsd) {
        this.statsd.increment('prober.' + this.title + '.probe.ok');
    }
};

Prober.prototype.setLogger = function setLogger(logger) {
    this.logger = logger;
};

Prober.prototype.probe = function probe(request, bypass, callback) {
    var self = this;

    if (!callback) {
        callback = bypass;
    }

    if (!this.enabled) {
        return request(callback);
    }

    // If the backend is healthy, or it's been enough time
    // that we should check to see if the backend is no longer
    // sick, then make a request to the backend.
    if (this.isHealthy() || this._isPityProbe()) {
        if (this.statsd) {
            this.statsd.increment('prober.' + this.title +
                '.request.performed');
        }

        var wrappedCallback;
        if (this.detectFailuresByCallback) {
            wrappedCallback = function(err, resp) {
                var errResponse = resp && !isNaN(resp.statusCode) &&
                    resp.statusCode >= 500;
                if (err || errResponse) {
                    self.notok();
                } else {
                    self.ok();
                }

                if (callback && typeof callback === 'function') {
                    callback.apply(null, arguments);
                }
            };
        }
        try {
            request(wrappedCallback);
        } catch (e) {
            // we can't log the error here in case the prober is used
            // within the logger. So instead we pass it to the failureHandler
            // the user of this module should not log in the failureHandler
            // maybe send an email instead
            this.failureHandler({
                subject: "Exception in Prober while probing " + this.title,
                body: e.stack
            });
        }

        this.lastBackendRequest = this.now();
    } else {
        if (this.statsd) {
            this.statsd.increment('prober.' + this.title + '.request.bypassed');
        }

        if (bypass && typeof bypass === 'function') {
            bypass(new Error(this.title + ' backend is unhealthy'));
        }
    }
};

Prober.prototype._addProbe = function addProbe(isOk) {
    var timestamp = this.now();
    var logger = this.logger;
    var statsd = this.statsd;

    var wasHealthy = this.isHealthy();
    var thisProbe = { isOk: isOk, timestamp: timestamp };
    this.probes.unshift(thisProbe);
    this.probes = this.probes.slice(0, this.window);
    var isHealthy = this.isHealthy();

    if (wasHealthy && !isHealthy) {
        if (logger) {
            logger.warn(this.title + ' has gotten sick');
        }
        if (statsd) {
            this.statsd.increment('prober.' + this.title + '.health.sick');
        }
    } else if (!wasHealthy && isHealthy) {
        this.waitPeriod = this.defaultWaitPeriod;
        if (logger) {
            logger.warn(this.title + ' has returned to health');
        }
        if (statsd) {
            this.statsd.increment('prober.' + this.title + '.health.recovered');
        }
    } else if (!wasHealthy && !isHealthy) {
        if (statsd) {
            this.statsd.increment('prober.' + this.title +
                '.health.still-sick');
        }

        if (thisProbe.isOk) {
            this.waitPeriod /= 2;
            if (logger) {
                logger.warn(this.title + ' is still sick but last probe was ' +
                    'healthy. Decreased wait period to ' +
                    this.waitPeriod + 'ms');
            }
        } else {
            this.waitPeriod *= 2;

            if (this.waitPeriod > this.maxWaitPeriod) {
                this.waitPeriod = this.maxWaitPeriod;
                if (logger) {
                    logger.warn(this.title + ' is still sick. Wait period is ' +
                        'at its max, ' + this.waitPeriod + 'ms');
                }
            } else if (logger) {
                logger.warn(this.title + ' is still sick. Increased wait ' +
                    'period to ' + this.waitPeriod + 'ms');
            }
        }
    }
};

Prober.prototype._getOks = function _getOks() {
    return this.probes.filter(function(probe) { return probe.isOk; });
};

Prober.prototype._isPityProbe = function _isPityProbe() {
    return this.lastBackendRequest && this.now() >=
        (this.lastBackendRequest + this.waitPeriod);
};

module.exports = Prober;
<|endoftext|>


<commit_before>module.exports = (function () {
  'use strict';

  // This is a function that can find the line in an input JSON string that corresponds to the
  // given JSON pointer.
  // More information about JSON pointers at: http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-08

  var _ = require('lodash');
  var pointer = require('json-pointer');

  var escapee = {
      '"': '"',
      '\\': '\\',
      '/': '/',
      b: '\b',
      f: '\f',
      n: '\n',
      r: '\r',
      t: '\t'
    },

    addSegment,
    error,
    parse,
    value,
    object,
    array,
    number,
    word,
    string,
    blank,
    next;

  parse = function (state) {
    value(state);
  };

  addSegment = function (segment, state) {
    state.currentJsonPointer.push(segment);
    if (_.isEqual(state.currentJsonPointer, state.targetJsonPointer)) {
      state.result = { line: state.currentLine, column: state.currentColumn };
    }
  };

  error = function (message, state) {
    throw {
      name: 'SyntaxError',
      message: message,
      at: state.currentIndex,
      text: state.text
    };
  };

  next = function (state, expected) {
    if (expected && expected !== state.currentChar) {
      error('Expected "' + expected + '" instead of "' + state.currentChar + '"', state);
    }

    state.currentChar = state.text.charAt(state.currentIndex);
    state.currentIndex++;
    state.currentColumn++;
    return state.currentChar;
  };

  number = function (state) {
    var number,
      string = '';

    if (state.currentChar === '-') {
      string = '-';
      next(state, '-');
    }
    while (state.currentChar >= '0' && state.currentChar <= '9') {
      string += state.currentChar;
      next(state);
    }
    if (state.currentChar === '.') {
      string += '.';
      while (next(state) && state.currentChar >= '0' && state.currentChar <= '9') {
        string += state.currentChar;
      }
    }
    if (state.currentChar === 'e' || state.currentChar === 'E') {
      string += state.currentChar;
      next(state);
      if (state.currentChar === '-' || state.currentChar === '+') {
        string += state.currentChar;
        next(state);
      }
      while (state.currentChar >= '0' && state.currentChar <= '9') {
        string += state.currentChar;
        next(state);
      }
    }
    number = +string;
    if (!isFinite(number)) {
      error('Bad number', state);
    }
  };

  string = function (state) {

    // Parse a string value.

    var hex,
      i,
      string = '',
      uffff;

    // When parsing for string values, we must look for " and \ characters.

    if (state.currentChar === '"') {
      while (next(state)) {
        if (state.currentChar === '"') {
          next(state);
          return string;
        }
        if (state.currentChar === '\\') {
          next(state);
          if (state.currentChar === 'u') {
            uffff = 0;
            for (i = 0; i < 4; i += 1) {
              hex = parseInt(next(state), 16);
              if (!isFinite(hex)) {
                break;
              }
              uffff = uffff * 16 + hex;
            }
            string += String.fromCharCode(uffff);
          } else if (typeof escapee[ state.currentChar ] === 'string') {
            string += escapee[ state.currentChar ];
          } else {
            break;
          }
        } else {
          string += state.currentChar;
        }
      }
    }
    error('Bad string', state);
  };

  blank = function (state) {
    while (state.currentChar && state.currentChar <= ' ') {
      if (state.currentChar === '\n') {
        state.currentLine++;
        state.currentColumn = 1;
      }
      next(state);
    }
  };

  word = function (state) {

    switch (state.currentChar) {
      case 't':
        next(state, 't');
        next(state, 'r');
        next(state, 'u');
        next(state, 'e');
        return;
      case 'f':
        next(state, 'f');
        next(state, 'a');
        next(state, 'l');
        next(state, 's');
        next(state, 'e');
        return;
      case 'n':
        next(state, 'n');
        next(state, 'u');
        next(state, 'l');
        next(state, 'l');
        return;
    }
    error('Unexpected "' + state.currentChar + '"', state);
  };

  object = function (state) {
    if (state.result) {
      return;
    }

    var key,
      object = {};

    if (state.currentChar === '{') {
      next(state, '{');
      blank(state);
      if (state.currentChar === '}') {
        next(state, '}');
        return;
      }
      while (state.currentChar) {
        key = string(state);
        addSegment(key, state);

        if (state.result) {
          return;
        }

        blank(state);
        next(state, ':');
        if (Object.hasOwnProperty.call(object, key)) {
          error('Duplicate key "' + key + '"', state);
        }
        object[ key ] = value(state);

        if (state.result) {
          return;
        }

        state.currentJsonPointer.pop();
        blank(state);
        if (state.currentChar === '}') {
          next(state, '}');
          return;
        }
        next(state, ',');
        blank(state);
      }
    }
    error('Bad object', state);
  };

  array = function (state) {
    if (state.result) {
      return;
    }

    var arrayIndex = -1;

    if (state.currentChar === '[') {
      next(state, '[');
      blank(state);
      if (state.currentChar === ']') {
        next(state, ']');
        state.currentJsonPointer.pop();
        return;
      }

      while (state.currentChar) {
        arrayIndex++;
        addSegment('' + arrayIndex, state);

        if (state.result) {
          return;
        }

        value(state);

        if (state.result) {
          return;
        }

        blank(state);
        state.currentJsonPointer.pop();
        if (state.currentChar === ']') {
          next(state, ']');
          return;
        }
        next(state, ',');
        blank(state);
      }
    }
    error('Bad array', state);
  };

  value = function (state) {
    if (state.result) {
      return;
    }

    blank(state);
    switch (state.currentChar) {
      case '{':
        object(state);
        return;
      case '[':
        array(state);
        return;
      case '"':
        string(state);
        return;
      case '-':
        number(state);
        return;
      default:
        if (state.currentChar >= '0'
          && state.currentChar <= '9') {
          number(state);
        } else {
          word(state);
        }
        return;
    }
  };

  return {
    getLineNumber: function (source, jsonPointer) {
      var state = {
        currentIndex: 0,
        currentChar: ' ',
        currentLine: 1,
        currentColumn: 1,
        targetJsonPointer: pointer.parse(jsonPointer),
        currentJsonPointer: [],
        result: null,
        text: source,
      };

      if (_.isEmpty(state.targetJsonPointer) || '/' === jsonPointer) {
        return { line: 1, column: 1 };
      }

      parse(state);
      return state.result;
    }
  };
}());
<commit_msg>:art: Remove useless comments<commit_after>module.exports = (function () {
  'use strict';

  // This is a function that can find the line in an input JSON string that corresponds to the
  // given JSON pointer.
  // More information about JSON pointers at: http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-08

  var _ = require('lodash');
  var pointer = require('json-pointer');

  var escapee = {
      '"': '"',
      '\\': '\\',
      '/': '/',
      b: '\b',
      f: '\f',
      n: '\n',
      r: '\r',
      t: '\t'
    },

    addSegment,
    error,
    parse,
    value,
    object,
    array,
    number,
    word,
    string,
    blank,
    next;

  parse = function (state) {
    value(state);
  };

  addSegment = function (segment, state) {
    state.currentJsonPointer.push(segment);
    if (_.isEqual(state.currentJsonPointer, state.targetJsonPointer)) {
      state.result = { line: state.currentLine, column: state.currentColumn };
    }
  };

  error = function (message, state) {
    throw {
      name: 'SyntaxError',
      message: message,
      at: state.currentIndex,
      text: state.text
    };
  };

  next = function (state, expected) {
    if (expected && expected !== state.currentChar) {
      error('Expected "' + expected + '" instead of "' + state.currentChar + '"', state);
    }

    state.currentChar = state.text.charAt(state.currentIndex);
    state.currentIndex++;
    state.currentColumn++;
    return state.currentChar;
  };

  number = function (state) {
    var number,
      string = '';

    if (state.currentChar === '-') {
      string = '-';
      next(state, '-');
    }
    while (state.currentChar >= '0' && state.currentChar <= '9') {
      string += state.currentChar;
      next(state);
    }
    if (state.currentChar === '.') {
      string += '.';
      while (next(state) && state.currentChar >= '0' && state.currentChar <= '9') {
        string += state.currentChar;
      }
    }
    if (state.currentChar === 'e' || state.currentChar === 'E') {
      string += state.currentChar;
      next(state);
      if (state.currentChar === '-' || state.currentChar === '+') {
        string += state.currentChar;
        next(state);
      }
      while (state.currentChar >= '0' && state.currentChar <= '9') {
        string += state.currentChar;
        next(state);
      }
    }
    number = +string;
    if (!isFinite(number)) {
      error('Bad number', state);
    }
  };

  string = function (state) {
    var hex,
      i,
      string = '',
      uffff;

    if (state.currentChar === '"') {
      while (next(state)) {
        if (state.currentChar === '"') {
          next(state);
          return string;
        }
        if (state.currentChar === '\\') {
          next(state);
          if (state.currentChar === 'u') {
            uffff = 0;
            for (i = 0; i < 4; i += 1) {
              hex = parseInt(next(state), 16);
              if (!isFinite(hex)) {
                break;
              }
              uffff = uffff * 16 + hex;
            }
            string += String.fromCharCode(uffff);
          } else if (typeof escapee[ state.currentChar ] === 'string') {
            string += escapee[ state.currentChar ];
          } else {
            break;
          }
        } else {
          string += state.currentChar;
        }
      }
    }
    error('Bad string', state);
  };

  blank = function (state) {
    while (state.currentChar && state.currentChar <= ' ') {
      if (state.currentChar === '\n') {
        state.currentLine++;
        state.currentColumn = 1;
      }
      next(state);
    }
  };

  word = function (state) {

    switch (state.currentChar) {
      case 't':
        next(state, 't');
        next(state, 'r');
        next(state, 'u');
        next(state, 'e');
        return;
      case 'f':
        next(state, 'f');
        next(state, 'a');
        next(state, 'l');
        next(state, 's');
        next(state, 'e');
        return;
      case 'n':
        next(state, 'n');
        next(state, 'u');
        next(state, 'l');
        next(state, 'l');
        return;
    }
    error('Unexpected "' + state.currentChar + '"', state);
  };

  object = function (state) {
    if (state.result) {
      return;
    }

    var key,
      object = {};

    if (state.currentChar === '{') {
      next(state, '{');
      blank(state);
      if (state.currentChar === '}') {
        next(state, '}');
        return;
      }
      while (state.currentChar) {
        key = string(state);
        addSegment(key, state);

        if (state.result) {
          return;
        }

        blank(state);
        next(state, ':');
        if (Object.hasOwnProperty.call(object, key)) {
          error('Duplicate key "' + key + '"', state);
        }
        object[ key ] = value(state);

        if (state.result) {
          return;
        }

        state.currentJsonPointer.pop();
        blank(state);
        if (state.currentChar === '}') {
          next(state, '}');
          return;
        }
        next(state, ',');
        blank(state);
      }
    }
    error('Bad object', state);
  };

  array = function (state) {
    if (state.result) {
      return;
    }

    var arrayIndex = -1;

    if (state.currentChar === '[') {
      next(state, '[');
      blank(state);
      if (state.currentChar === ']') {
        next(state, ']');
        state.currentJsonPointer.pop();
        return;
      }

      while (state.currentChar) {
        arrayIndex++;
        addSegment('' + arrayIndex, state);

        if (state.result) {
          return;
        }

        value(state);

        if (state.result) {
          return;
        }

        blank(state);
        state.currentJsonPointer.pop();
        if (state.currentChar === ']') {
          next(state, ']');
          return;
        }
        next(state, ',');
        blank(state);
      }
    }
    error('Bad array', state);
  };

  value = function (state) {
    if (state.result) {
      return;
    }

    blank(state);
    switch (state.currentChar) {
      case '{':
        object(state);
        return;
      case '[':
        array(state);
        return;
      case '"':
        string(state);
        return;
      case '-':
        number(state);
        return;
      default:
        if (state.currentChar >= '0'
          && state.currentChar <= '9') {
          number(state);
        } else {
          word(state);
        }
        return;
    }
  };

  return {
    getLineNumber: function (source, jsonPointer) {
      var state = {
        currentIndex: 0,
        currentChar: ' ',
        currentLine: 1,
        currentColumn: 1,
        targetJsonPointer: pointer.parse(jsonPointer),
        currentJsonPointer: [],
        result: null,
        text: source,
      };

      if (_.isEmpty(state.targetJsonPointer) || '/' === jsonPointer) {
        return { line: 1, column: 1 };
      }

      parse(state);
      return state.result;
    }
  };
}());
<|endoftext|>


<commit_before>/**
 * Build and run BDD flows with before/after hooks, describe, it
 *
 * Licensed under MIT.
 * Copyright (c) 2013 David Smith <https://github.com/codeactual/>
 */

/*jshint node:true*/
'use strict';

module.exports = {
  Bddflow: Bddflow,
  create: create,
  require: require // Allow tests to use component-land require.
};

var Batch = require('batch');
var bind = require('bind');
var clone = require('clone');
var configurable = require('configurable.js');
var extend = require('extend');

// Match properties that should not be 'inherited' by it(), hooks, or all.
var defOmitContextRegex = {
  all: [/^__|^(it|describe|before|beforeEach|after|afterEach|settings|set|get)$/],
  it: [],
  hook: []
};

function create() { return new Bddflow(); }

/**
 * Flow configuration and execution.
 */
function Bddflow() {
  this.settings = {
    done: noOp, // Batch#end callback that fires after flow completes.

    // Propagate to each new Describe instance:
    itWrap: null,
    describeWrap: null,
    omitContextRegex: clone(defOmitContextRegex),
    path: [], // Names of ancestor describe levels to the currently executing it().
    grep: /.?/, // Filters it() execution by "current path + it() name".
    grepv: null // Omits it() execution by "current path + it() name".
  };
  this.rootDescribes = [];
  this.batch = new Batch();
  this.seedProps = {}; // Will me merged into initial hook/describe/it context.
  this.running = false;
}
Bddflow.sharedConfigKeys = [
  'describeWrap', 'itWrap', 'omitContextRegex', 'path', 'grep', 'grepv'
];

configurable(Bddflow.prototype);

/**
 * Add a property to the initial hook/describe/it shared context.
 *
 * @param {string} key
 * @param {mixed} val
 * @return {object} this
 */
Bddflow.prototype.addContextProp = function(key, val) {
  this.seedProps[key] = val;
  return this;
};

/**
 * Add a top-level describe().
 *
 * @param {string} name
 * @param {function} cb
 */
Bddflow.prototype.addRootDescribe = function(name, cb) {
  var self = this;
  var desc = new Describe(name);

  var bddFlowConfig = {}; // Propagate selected settings.
  Bddflow.sharedConfigKeys.forEach(function(key) {
    bddFlowConfig[key] = self.get(key);
  });
  desc.set('bddFlowConfig', bddFlowConfig);

  desc.describe(name, cb);
  this.rootDescribes.push(desc);
  return this;
};

/**
 * Prevent a type of flow function from 'inheriting' specific context properties
 * from enclosing/subsequently-executed flow functions.
 *
 * @param {string} 'it', 'hook'
 * @param {object} RegExp instance.
 * @return {object} this
 */
Bddflow.prototype.hideContextProp = function(type, regex) {
  this.get('omitContextRegex')[type].push(regex);
  return this;
};

/**
 * Check if run() has been called.
 *
 * @return {boolean}
 */
Bddflow.prototype.isRunning = function() {
  return this.running;
};

/**
 * Run collected describe() layers.
 */
Bddflow.prototype.run = function() {
  var self = this;

  this.running = true;

  var batch = new Batch();
  batch.concurrency = 1;
  this.rootDescribes.forEach(function(desc) {
    batch.push(function(taskDone) {
      extend(desc, self.seedProps);
      runSteps(desc.__steps, taskDone);
    });
  });
  batch.end(this.get('done'));
};

/**
 * Filter 'this' into an object with properties that can be 'inherited'
 * between hooks/describe/it.
 *
 * Static used in other classes via call(). Exposed for test access.
 *
 * @param {string} type 'describe', 'it', 'hook'
 * @return {object}
 */
Bddflow.getInheritableContext = function(type) {
  var self = this;
  var omitContextRegex = this.get('bddFlowConfig').omitContextRegex;
  var regex = omitContextRegex.all.concat(type ? omitContextRegex[type] : []);

  return Object.keys(this).reduce(function(memo, key) {
    var omit = false;
    regex.forEach(function(re) {
      omit = omit || re.test(key);
    });
    if (omit) {
      return memo;
    }
    memo[key] = self[key];
    return memo;
  }, {});
};

/**
 * Augment 'this' with a new non-enumerable/configrable property intended
 * for internal-use only. Help avoid conflicts with properties 'inherited'
 * via getInheritableContext.
 *
 * Static used in other classes via call(). Exposed for test access.
 *
 * @param {string} key
 * @param {mixed} value
 */
Bddflow.addInternalProp = function(key, value) {
  Object.defineProperty(
    this, '__' + key,
    {value: value, enumerable: false, configurable: false, writable: true}
  );
};

/**
 * Callback context for before(), beforeEach(), etc.
 *
 * @param {string} name Of enclosing describe().
 */
function HookContext(name) {
  Bddflow.addInternalProp.call(this, 'name', name);
}
configurable(HookContext.prototype);
HookContext.prototype.getInheritableContext = Bddflow.getInheritableContext;

// Auto-terminating callback for use with Batch#push.
Bddflow.defaultHookImpl = function(done) { done(); };

/**
 * Container for a before(), beforeEach(), etc. method set.
 */
function HookSet() {
  this.before = Bddflow.defaultHookImpl;
  this.beforeEach = Bddflow.defaultHookImpl;
  this.after = Bddflow.defaultHookImpl;
  this.afterEach = Bddflow.defaultHookImpl;
}

/**
 * @param {string} name Subject expectation.
 * @param {string} name Test subject.
 */
function ItCallback(name, cb) {
  Bddflow.addInternalProp.call(this, 'name', name);
  this.cb = cb;
}

/**
 * A describe()'s properties, internal hooks, and nested steps (describe/it).
 *
 * @param {string} name Subject expected to exhibit some behavior.
 */
function Describe(name) {
  Bddflow.addInternalProp.call(this, 'name', name);
  Bddflow.addInternalProp.call(this, 'steps', []);
  Bddflow.addInternalProp.call(this, 'hooks', new HookSet());
  this.settings = {
    bddFlowConfig: {}
  };
}
configurable(Describe.prototype);
Describe.prototype.getInheritableContext = Bddflow.getInheritableContext;

/**
 * Add an it() step.
 *
 * @param {string} name
 * @param {function} cb Batch#push compat.
 */
Describe.prototype.it = function(name, cb) {
  this.__steps.push(new ItCallback(name, cb));
};

/**
 * Add a describe() step.
 *
 * @param {string} name
 * @param {function} cb Batch#push compat.
 */
Describe.prototype.describe = function(name, cb) {
  var self = this;
  var step = function(done) {
    var desc = new Describe(name); // Collect nested steps.
    var bddFlowConfig = clone(self.get('bddFlowConfig'));
    bddFlowConfig.path.push(name);
    desc.set('bddFlowConfig', bddFlowConfig);
    extend(desc, self.getInheritableContext());

    var describeWrap = self.get('bddFlowConfig').describeWrap || defDescribeWrap;
    describeWrap(name, function() {
      var wrapContext = this;
      var mergedContext = extend(desc, wrapContext);
      cb.call(mergedContext);
    });

    var hc = new HookContext(name);
    hc.set('bddFlowConfig', self.get('bddFlowConfig'));

    var batch = new Batch();

    batch.push(function(done) {
      extend(hc, desc.getInheritableContext('hook'));
      function asyncCb() {
        extend(desc, hc.getInheritableContext('hook'));
        done();
      }
      var hook = desc.__hooks.before;
      if (hook.length) { // Expects callback arg.
        desc.__hooks.before.call(hc, asyncCb);
      } else {
        desc.__hooks.before.call(hc);
        asyncCb();
      }
    });

    batch.push(function(done) { // Wrap hooks around each internal describe()/it()
      desc.__steps = desc.__steps.map(function(step) {
        if (step instanceof DescribeCallback) {
          extend(desc, hc.getInheritableContext());
          return new DescribeCallback(step.__name, bind(desc, step.cb));
        }

        var itPath = bddFlowConfig.path.concat(step.__name);
        if (bddFlowConfig.grepv) {
          if (bddFlowConfig.grepv.test(itPath.join(' '))) {
            return new ItCallback(step.__name, batchNoOp);
          }
        } else if (bddFlowConfig.grep) {
          if (!bddFlowConfig.grep.test(itPath.join(' '))) {
            return new ItCallback(step.__name, batchNoOp);
          }
        }

        return new DescribeCallback(step.__name, function(done) { // instanceof ItCallback
          var batch = new Batch();
          batch.push(function(done) {
            extend(hc, desc.getInheritableContext('hook'));
            function asyncCb() {
              extend(desc, hc.getInheritableContext('hook'));
              done();
            }
            var hook = desc.__hooks.beforeEach;
            if (hook.length) { // Expects callback arg.
              desc.__hooks.beforeEach.call(hc, asyncCb);
            } else {
              desc.__hooks.beforeEach.call(hc);
              asyncCb();
            }
          });
          batch.push(function(done) {
            var itContext = {};
            extend(itContext, desc.getInheritableContext('it'));
            itContext.__name = step.__name;
            itContext.__path = itPath;

            var itWrap = self.get('bddFlowConfig').itWrap || defItWrap;
            itWrap(step.__name, function() {
              var wrapContext = this;
              var mergedContext = extend(itContext, wrapContext);
              if (step.cb.length) { // Expects callback arg.
                step.cb.call(mergedContext, done);
              } else {
                step.cb.call(mergedContext);
                done();
              }
            });
          });
          batch.push(function(done) {
            function asyncCb() {
              extend(desc, hc.getInheritableContext('hook'));
              done();
            }
            var hook = desc.__hooks.afterEach;
            if (hook.length) { // Expects callback arg.
              desc.__hooks.afterEach.call(hc, asyncCb);
            } else {
              desc.__hooks.afterEach.call(hc);
              asyncCb();
            }
          });
          batch.concurrency = 1;
          batch.end(done);
        });
      });

      runSteps(desc.__steps, done);
    });

    batch.push(function(done) {
      extend(hc, desc.getInheritableContext('hook'));
      function asyncCb() {
        extend(desc, hc.getInheritableContext('hook'));
        done();
      }
      var hook = desc.__hooks.after;
      if (hook.length) { // Expects callback arg.
        desc.__hooks.after.call(hc, asyncCb);
      } else {
        desc.__hooks.after.call(hc);
        asyncCb();
      }
    });

    batch.concurrency = 1;
    batch.end(done);
  };
  this.__steps.push(new DescribeCallback(name, step));
};

/**
 * Override the default no-op before() hook.
 *
 * @param {function} cb
 */
Describe.prototype.before = function(cb) { this.__hooks.before = cb; };

/**
 * Override the default no-op beforeEach() hook.
 *
 * @param {function} cb
 */
Describe.prototype.beforeEach = function(cb) { this.__hooks.beforeEach = cb; };

/**
 * Override the default no-op after() hook.
 *
 * @param {function} cb
 */
Describe.prototype.after = function(cb) { this.__hooks.after = cb; };

/**
 * Override the default no-op afterEach() hook.
 *
 * @param {function} cb
 */
Describe.prototype.afterEach = function(cb) { this.__hooks.afterEach = cb; };

/**
 * @param {string} name Test subject.
 * @param {function} cb
 */
function DescribeCallback(name, cb) {
  Bddflow.addInternalProp.call(this, 'name', name);
  this.cb = cb;
}

/**
 * Execute an array of functions w/ Batch.
 *
 * @param {array} steps
 * @param {function} cb Called at completion.
 * @param {number} [concurrency=1]
 */
function runSteps(steps, cb) {
  var batch = new Batch();
  batch.concurrency = 1;
  steps.forEach(function(step) { batch.push(step.cb); });
  batch.end(cb);
}

function noOp() {}
function batchNoOp(taskDone) { taskDone(); }
function defItWrap(name, cb) { cb(); }
function defDescribeWrap(name, cb) { cb(); }
<commit_msg>feat(Bddflow): Support hideContextProp() string arg<commit_after>/**
 * Build and run BDD flows with before/after hooks, describe, it
 *
 * Licensed under MIT.
 * Copyright (c) 2013 David Smith <https://github.com/codeactual/>
 */

/*jshint node:true*/
'use strict';

module.exports = {
  Bddflow: Bddflow,
  create: create,
  require: require // Allow tests to use component-land require.
};

var Batch = require('batch');
var bind = require('bind');
var clone = require('clone');
var configurable = require('configurable.js');
var extend = require('extend');

// Match properties that should not be 'inherited' by it(), hooks, or all.
var defOmitContextRegex = {
  all: [/^__|^(it|describe|before|beforeEach|after|afterEach|settings|set|get)$/],
  it: [],
  hook: []
};

function create() { return new Bddflow(); }

/**
 * Flow configuration and execution.
 */
function Bddflow() {
  this.settings = {
    done: noOp, // Batch#end callback that fires after flow completes.

    // Propagate to each new Describe instance:
    itWrap: null,
    describeWrap: null,
    omitContextRegex: clone(defOmitContextRegex),
    path: [], // Names of ancestor describe levels to the currently executing it().
    grep: /.?/, // Filters it() execution by "current path + it() name".
    grepv: null // Omits it() execution by "current path + it() name".
  };
  this.rootDescribes = [];
  this.batch = new Batch();
  this.seedProps = {}; // Will me merged into initial hook/describe/it context.
  this.running = false;
}
Bddflow.sharedConfigKeys = [
  'describeWrap', 'itWrap', 'omitContextRegex', 'path', 'grep', 'grepv'
];

configurable(Bddflow.prototype);

/**
 * Add a property to the initial hook/describe/it shared context.
 *
 * @param {string} key
 * @param {mixed} val
 * @return {object} this
 */
Bddflow.prototype.addContextProp = function(key, val) {
  this.seedProps[key] = val;
  return this;
};

/**
 * Add a top-level describe().
 *
 * @param {string} name
 * @param {function} cb
 */
Bddflow.prototype.addRootDescribe = function(name, cb) {
  var self = this;
  var desc = new Describe(name);

  var bddFlowConfig = {}; // Propagate selected settings.
  Bddflow.sharedConfigKeys.forEach(function(key) {
    bddFlowConfig[key] = self.get(key);
  });
  desc.set('bddFlowConfig', bddFlowConfig);

  desc.describe(name, cb);
  this.rootDescribes.push(desc);
  return this;
};

/**
 * Prevent a type of flow function from 'inheriting' specific context properties
 * from enclosing/subsequently-executed flow functions.
 *
 * @param {string} 'it', 'hook'
 * @param {object} RegExp instance.
 * @return {object} this
 */
Bddflow.prototype.hideContextProp = function(type, regex) {
  if (typeof regex === 'string') {
    regex = new RegExp('^' + regex + '$');
  }
  this.get('omitContextRegex')[type].push(regex);
  return this;
};

/**
 * Check if run() has been called.
 *
 * @return {boolean}
 */
Bddflow.prototype.isRunning = function() {
  return this.running;
};

/**
 * Run collected describe() layers.
 */
Bddflow.prototype.run = function() {
  var self = this;

  this.running = true;

  var batch = new Batch();
  batch.concurrency = 1;
  this.rootDescribes.forEach(function(desc) {
    batch.push(function(taskDone) {
      extend(desc, self.seedProps);
      runSteps(desc.__steps, taskDone);
    });
  });
  batch.end(this.get('done'));
};

/**
 * Filter 'this' into an object with properties that can be 'inherited'
 * between hooks/describe/it.
 *
 * Static used in other classes via call(). Exposed for test access.
 *
 * @param {string} type 'describe', 'it', 'hook'
 * @return {object}
 */
Bddflow.getInheritableContext = function(type) {
  var self = this;
  var omitContextRegex = this.get('bddFlowConfig').omitContextRegex;
  var regex = omitContextRegex.all.concat(type ? omitContextRegex[type] : []);

  return Object.keys(this).reduce(function(memo, key) {
    var omit = false;
    regex.forEach(function(re) {
      omit = omit || re.test(key);
    });
    if (omit) {
      return memo;
    }
    memo[key] = self[key];
    return memo;
  }, {});
};

/**
 * Augment 'this' with a new non-enumerable/configrable property intended
 * for internal-use only. Help avoid conflicts with properties 'inherited'
 * via getInheritableContext.
 *
 * Static used in other classes via call(). Exposed for test access.
 *
 * @param {string} key
 * @param {mixed} value
 */
Bddflow.addInternalProp = function(key, value) {
  Object.defineProperty(
    this, '__' + key,
    {value: value, enumerable: false, configurable: false, writable: true}
  );
};

/**
 * Callback context for before(), beforeEach(), etc.
 *
 * @param {string} name Of enclosing describe().
 */
function HookContext(name) {
  Bddflow.addInternalProp.call(this, 'name', name);
}
configurable(HookContext.prototype);
HookContext.prototype.getInheritableContext = Bddflow.getInheritableContext;

// Auto-terminating callback for use with Batch#push.
Bddflow.defaultHookImpl = function(done) { done(); };

/**
 * Container for a before(), beforeEach(), etc. method set.
 */
function HookSet() {
  this.before = Bddflow.defaultHookImpl;
  this.beforeEach = Bddflow.defaultHookImpl;
  this.after = Bddflow.defaultHookImpl;
  this.afterEach = Bddflow.defaultHookImpl;
}

/**
 * @param {string} name Subject expectation.
 * @param {string} name Test subject.
 */
function ItCallback(name, cb) {
  Bddflow.addInternalProp.call(this, 'name', name);
  this.cb = cb;
}

/**
 * A describe()'s properties, internal hooks, and nested steps (describe/it).
 *
 * @param {string} name Subject expected to exhibit some behavior.
 */
function Describe(name) {
  Bddflow.addInternalProp.call(this, 'name', name);
  Bddflow.addInternalProp.call(this, 'steps', []);
  Bddflow.addInternalProp.call(this, 'hooks', new HookSet());
  this.settings = {
    bddFlowConfig: {}
  };
}
configurable(Describe.prototype);
Describe.prototype.getInheritableContext = Bddflow.getInheritableContext;

/**
 * Add an it() step.
 *
 * @param {string} name
 * @param {function} cb Batch#push compat.
 */
Describe.prototype.it = function(name, cb) {
  this.__steps.push(new ItCallback(name, cb));
};

/**
 * Add a describe() step.
 *
 * @param {string} name
 * @param {function} cb Batch#push compat.
 */
Describe.prototype.describe = function(name, cb) {
  var self = this;
  var step = function(done) {
    var desc = new Describe(name); // Collect nested steps.
    var bddFlowConfig = clone(self.get('bddFlowConfig'));
    bddFlowConfig.path.push(name);
    desc.set('bddFlowConfig', bddFlowConfig);
    extend(desc, self.getInheritableContext());

    var describeWrap = self.get('bddFlowConfig').describeWrap || defDescribeWrap;
    describeWrap(name, function() {
      var wrapContext = this;
      var mergedContext = extend(desc, wrapContext);
      cb.call(mergedContext);
    });

    var hc = new HookContext(name);
    hc.set('bddFlowConfig', self.get('bddFlowConfig'));

    var batch = new Batch();

    batch.push(function(done) {
      extend(hc, desc.getInheritableContext('hook'));
      function asyncCb() {
        extend(desc, hc.getInheritableContext('hook'));
        done();
      }
      var hook = desc.__hooks.before;
      if (hook.length) { // Expects callback arg.
        desc.__hooks.before.call(hc, asyncCb);
      } else {
        desc.__hooks.before.call(hc);
        asyncCb();
      }
    });

    batch.push(function(done) { // Wrap hooks around each internal describe()/it()
      desc.__steps = desc.__steps.map(function(step) {
        if (step instanceof DescribeCallback) {
          extend(desc, hc.getInheritableContext());
          return new DescribeCallback(step.__name, bind(desc, step.cb));
        }

        var itPath = bddFlowConfig.path.concat(step.__name);
        if (bddFlowConfig.grepv) {
          if (bddFlowConfig.grepv.test(itPath.join(' '))) {
            return new ItCallback(step.__name, batchNoOp);
          }
        } else if (bddFlowConfig.grep) {
          if (!bddFlowConfig.grep.test(itPath.join(' '))) {
            return new ItCallback(step.__name, batchNoOp);
          }
        }

        return new DescribeCallback(step.__name, function(done) { // instanceof ItCallback
          var batch = new Batch();
          batch.push(function(done) {
            extend(hc, desc.getInheritableContext('hook'));
            function asyncCb() {
              extend(desc, hc.getInheritableContext('hook'));
              done();
            }
            var hook = desc.__hooks.beforeEach;
            if (hook.length) { // Expects callback arg.
              desc.__hooks.beforeEach.call(hc, asyncCb);
            } else {
              desc.__hooks.beforeEach.call(hc);
              asyncCb();
            }
          });
          batch.push(function(done) {
            var itContext = {};
            extend(itContext, desc.getInheritableContext('it'));
            itContext.__name = step.__name;
            itContext.__path = itPath;

            var itWrap = self.get('bddFlowConfig').itWrap || defItWrap;
            itWrap(step.__name, function() {
              var wrapContext = this;
              var mergedContext = extend(itContext, wrapContext);
              if (step.cb.length) { // Expects callback arg.
                step.cb.call(mergedContext, done);
              } else {
                step.cb.call(mergedContext);
                done();
              }
            });
          });
          batch.push(function(done) {
            function asyncCb() {
              extend(desc, hc.getInheritableContext('hook'));
              done();
            }
            var hook = desc.__hooks.afterEach;
            if (hook.length) { // Expects callback arg.
              desc.__hooks.afterEach.call(hc, asyncCb);
            } else {
              desc.__hooks.afterEach.call(hc);
              asyncCb();
            }
          });
          batch.concurrency = 1;
          batch.end(done);
        });
      });

      runSteps(desc.__steps, done);
    });

    batch.push(function(done) {
      extend(hc, desc.getInheritableContext('hook'));
      function asyncCb() {
        extend(desc, hc.getInheritableContext('hook'));
        done();
      }
      var hook = desc.__hooks.after;
      if (hook.length) { // Expects callback arg.
        desc.__hooks.after.call(hc, asyncCb);
      } else {
        desc.__hooks.after.call(hc);
        asyncCb();
      }
    });

    batch.concurrency = 1;
    batch.end(done);
  };
  this.__steps.push(new DescribeCallback(name, step));
};

/**
 * Override the default no-op before() hook.
 *
 * @param {function} cb
 */
Describe.prototype.before = function(cb) { this.__hooks.before = cb; };

/**
 * Override the default no-op beforeEach() hook.
 *
 * @param {function} cb
 */
Describe.prototype.beforeEach = function(cb) { this.__hooks.beforeEach = cb; };

/**
 * Override the default no-op after() hook.
 *
 * @param {function} cb
 */
Describe.prototype.after = function(cb) { this.__hooks.after = cb; };

/**
 * Override the default no-op afterEach() hook.
 *
 * @param {function} cb
 */
Describe.prototype.afterEach = function(cb) { this.__hooks.afterEach = cb; };

/**
 * @param {string} name Test subject.
 * @param {function} cb
 */
function DescribeCallback(name, cb) {
  Bddflow.addInternalProp.call(this, 'name', name);
  this.cb = cb;
}

/**
 * Execute an array of functions w/ Batch.
 *
 * @param {array} steps
 * @param {function} cb Called at completion.
 * @param {number} [concurrency=1]
 */
function runSteps(steps, cb) {
  var batch = new Batch();
  batch.concurrency = 1;
  steps.forEach(function(step) { batch.push(step.cb); });
  batch.end(cb);
}

function noOp() {}
function batchNoOp(taskDone) { taskDone(); }
function defItWrap(name, cb) { cb(); }
function defDescribeWrap(name, cb) { cb(); }
<|endoftext|>


<commit_before>var path = require('path')
var async = require('async')
var _ = require('lodash')
var cev = require('config-cev-generator')
var debug = require('debug')('yaktor-config')

var globals
var servers
var yaktor = {
  start: function (configuration, callback) {
    if (typeof configuration === 'function') {
      callback = configuration
      configuration = {}
    }
    globals = require(path.resolve('config', 'global'))
    servers = require(path.resolve('config', 'servers'))

    // get default configuration values and put them on yaktor
    _.merge(yaktor, getConfigDefaults())
    debug('configuration defaults:')
    debug(JSON.stringify(yaktor, 0, 2))

    // now override from environment variables
    var envOverrides = getConfigEnvironmentVariables(yaktor)
    var serversEnvOverrides = envOverrides.servers || {}
    var globalEnvOverrides = _.cloneDeep(envOverrides)
    delete globalEnvOverrides.servers
    _.merge(yaktor, globalEnvOverrides)
    debug('configuration after overriding from environment variables:')
    debug(JSON.stringify(yaktor, 0, 2))

    // now override from given configuration parameter
    debug('given configuration object:')
    debug(JSON.stringify(configuration, 0, 2))
    var suppliedGlobalOverrides = _.cloneDeep(configuration)
    delete suppliedGlobalOverrides.servers
    _.merge(yaktor, suppliedGlobalOverrides)
    debug('resolved yaktor configuration:')
    debug(JSON.stringify(yaktor, 0, 2))

    // initialize logger
    var log = require('./logger').yaktorInit(yaktor)
    process.on('uncaughtException', function (err) {
      log.error('uncaught exception', err.stack)
    })

    async.series([
      async.apply(globals.init, yaktor), // call global initializers
      function (next) { // initialize all the servers
        yaktor.serverContexts = {}
        async.eachSeries(Object.keys(yaktor.servers), function (serverName, next) {
          var server = servers[ serverName ]
          var ctx = _.cloneDeep(server.settings)
          // override values from environment variables
          _.merge(ctx, serversEnvOverrides[ serverName ] || {})
          // override values from configuration parameter
          _.merge(ctx, configuration.servers && configuration.servers[ serverName ] || {})
          // this allows a server to get at all config via require('yaktor').serverContexts['...']....
          yaktor.serverContexts[ serverName ] = ctx
          ctx.serverName = serverName
          Object.keys(yaktor).forEach(function (setting) {
            switch (setting) {
              case 'start': // the function you're currently in
              case 'servers': // settings for all servers
              case 'serverContexts': // yaktor's bucket for all server contexts
                return // skip because these aren't appropriate for the current server context
              default:
                // else merge copy of global context into server context with server context winning any conflicts
                ctx[ setting ] = _.merge(_.cloneDeep(yaktor[ setting ]), ctx[ setting ] || {})
                return
            }
          })
          server.init(ctx, function (err) {
            if (err) log.error(new Error((err.stack ? err.stack : err.toString()) + '\nRethrown:').stack)
            next(err)
          })
        }, function (err) {
          if (err) next(err)

          require('./engine')([ path.resolve('conversations', 'js') ], function (err) {
            callback(err)
          })
        })
      }
    ], function (err) {
      callback(err)
    })
  }
}

var getConfigDefaults = function () {
  // get design-time default configurations
  var defaults = {}
  Object.keys(globals.settings).forEach(function (key) {
    defaults[ key ] = globals.settings[ key ]
  })

  defaults.servers = {}
  Object.keys(servers).forEach(function (serverName) {
    defaults.servers[ serverName ] = servers[ serverName ].settings
  })

  return defaults
}

var getConfigEnvironmentVariables = function (object) {
  var mappings = cev.generate(object || {}, {
    noPrefix: true
  })
  debug('environment variable mappings:')
  debug(JSON.stringify(mappings, 0, 2))

  var walkMappings = function (mappings, configPrefix) {
    configPrefix = configPrefix || []

    Object.keys(mappings).forEach(function (key) {
      switch (typeof mappings[ key ]) {
        case 'string':
          var envarName = mappings[ key ]
          var envarValue = process.env[ envarName ]
          if (envarValue) {
            mappings[ key ] = envarValue
            debug('replaced configuration setting "' + configPrefix.concat(key).join('.') + '" with value "' + envarValue + '" from environment variable "' + envarName + '"')
          }
          else {
            delete mappings[ key ]
            debug('no value found in environment variable "' + envarName + '" for configuration setting "' + configPrefix.concat(key).join('.') + '"')
          }
          break
        case 'object':
          mappings[ key ] = walkMappings(mappings[ key ], configPrefix.concat(key))
          if (Object.keys(mappings[ key ]).length === 0) {
            delete mappings[ key ]
          }
          break
      }
    })
    return mappings
  }
  return walkMappings(mappings)
}

module.exports = yaktor
<commit_msg>fix format<commit_after>var path = require('path')
var async = require('async')
var _ = require('lodash')
var cev = require('config-cev-generator')
var debug = require('debug')('yaktor-config')

var globals
var servers
var yaktor = {
  start: function (configuration, callback) {
    if (typeof configuration === 'function') {
      callback = configuration
      configuration = {}
    }
    globals = require(path.resolve('config', 'global'))
    servers = require(path.resolve('config', 'servers'))

    // get default configuration values and put them on yaktor
    _.merge(yaktor, getConfigDefaults())
    debug('configuration defaults:')
    debug(JSON.stringify(yaktor, 0, 2))

    // now override from environment variables
    var envOverrides = getConfigEnvironmentVariables(yaktor)
    var serversEnvOverrides = envOverrides.servers || {}
    var globalEnvOverrides = _.cloneDeep(envOverrides)
    delete globalEnvOverrides.servers
    _.merge(yaktor, globalEnvOverrides)
    debug('configuration after overriding from environment variables:')
    debug(JSON.stringify(yaktor, 0, 2))

    // now override from given configuration parameter
    debug('given configuration object:')
    debug(JSON.stringify(configuration, 0, 2))
    var suppliedGlobalOverrides = _.cloneDeep(configuration)
    delete suppliedGlobalOverrides.servers
    _.merge(yaktor, suppliedGlobalOverrides)
    debug('resolved yaktor configuration:')
    debug(JSON.stringify(yaktor, 0, 2))

    // initialize logger
    var log = require('./logger').yaktorInit(yaktor)
    process.on('uncaughtException', function (err) {
      log.error('uncaught exception', err.stack)
    })

    async.series([
      async.apply(globals.init, yaktor), // call global initializers
      function (next) { // initialize all the servers
        yaktor.serverContexts = {}
        async.eachSeries(Object.keys(yaktor.servers), function (serverName, next) {
          var server = servers[ serverName ]
          var ctx = _.cloneDeep(server.settings)
          // override values from environment variables
          _.merge(ctx, serversEnvOverrides[ serverName ] || {})
          // override values from configuration parameter
          _.merge(ctx, configuration.servers && configuration.servers[ serverName ] || {})
          // this allows a server to get at all config via require('yaktor').serverContexts['...']....
          yaktor.serverContexts[ serverName ] = ctx
          ctx.serverName = serverName
          Object.keys(yaktor).forEach(function (setting) {
            switch (setting) {
              case 'start': // the function you're currently in
              case 'servers': // settings for all servers
              case 'serverContexts': // yaktor's bucket for all server contexts
                return // skip because these aren't appropriate for the current server context
              default:
                // else merge copy of global context into server context with server context winning any conflicts
                ctx[ setting ] = _.merge(_.cloneDeep(yaktor[ setting ]), ctx[ setting ] || {})
                return
            }
          })
          server.init(ctx, function (err) {
            if (err) log.error(new Error((err.stack ? err.stack : err.toString()) + '\nRethrown:').stack)
            next(err)
          })
        }, function (err) {
          if (err) next(err)

          require('./engine')([ path.resolve('conversations', 'js') ], function (err) {
            callback(err)
          })
        })
      }
    ], function (err) {
      callback(err)
    })
  }
}

var getConfigDefaults = function () {
  // get design-time default configurations
  var defaults = {}
  Object.keys(globals.settings).forEach(function (key) {
    defaults[ key ] = globals.settings[ key ]
  })

  defaults.servers = {}
  Object.keys(servers).forEach(function (serverName) {
    defaults.servers[ serverName ] = servers[ serverName ].settings
  })

  return defaults
}

var getConfigEnvironmentVariables = function (object) {
  var mappings = cev.generate(object || {}, {
    noPrefix: true
  })
  debug('environment variable mappings:')
  debug(JSON.stringify(mappings, 0, 2))

  var walkMappings = function (mappings, configPrefix) {
    configPrefix = configPrefix || []

    Object.keys(mappings).forEach(function (key) {
      switch (typeof mappings[ key ]) {
        case 'string':
          var envarName = mappings[ key ]
          var envarValue = process.env[ envarName ]
          if (envarValue) {
            mappings[ key ] = envarValue
            debug('replaced configuration setting "' + configPrefix.concat(key).join('.') + '" with value "' + envarValue + '" from environment variable "' + envarName + '"')
          } else {
            delete mappings[ key ]
            debug('no value found in environment variable "' + envarName + '" for configuration setting "' + configPrefix.concat(key).join('.') + '"')
          }
          break
        case 'object':
          mappings[ key ] = walkMappings(mappings[ key ], configPrefix.concat(key))
          if (Object.keys(mappings[ key ]).length === 0) {
            delete mappings[ key ]
          }
          break
      }
    })
    return mappings
  }
  return walkMappings(mappings)
}

module.exports = yaktor
<|endoftext|>


<commit_before>

var EventEmitter = require('events').EventEmitter

var inherits = require('util').inherits

inherits(Graphmitter, EventEmitter)

module.exports = Graphmitter

function each(obj, iter) {
  for(var k in obj) iter(k, obj[k])
}

function count(obj) {
  var c = 0
  for(var k in obj) c++
  return c
}

//
// Node / Vertice
//

function Node () {
  this.edges = {}
}

var nproto = Node.prototype

//returns the old data for this edge..
nproto.edge = function (to, data) {
  var _data = this.edges[to]
  this.edges[to] = data
  return _data
}

//also returns the old data for this edge..
nproto.del = function (to, data) {
  var _data = this.edges[to]
  delete this.edges[to]
  return _data
}

nproto.each = function (iter) {
  each(this.edges, iter)
  return this
}

//
// the whole graph
//

function Graphmitter () {
  if(!(this instanceof Graphmitter)) return new Graphmitter()
  this.nodes = {}
}

var proto = Graphmitter.prototype

proto.node = function (n) {
  return this.nodes[n] = this.nodes[n] || new Node(n)
}

proto.edge = function (from, to, data) {
  this.node(from)
  this.node(to)
  var _data = this.node(from).edge(to, data || true)
  if(_data !== data)
    this.emit('edge', from, to, data, _data)
  return this
}

proto.del = function (from, to) {
  var data = this.node(from).del(to)
  if (typeof data !== 'undefined')
    this.emit('del', from, to, data)
  return this
}

proto.each = function (iter) {
  each(this.nodes, iter)
  return this
}

proto.toJSON = function (iter) {
  var g = {}
  this.each(function (k, v) {
    var e = {}
    v.each(function (k, v) {
      e[k] = v
    })
    g[k] = e
  })
  return g
}

//
// graph generators
//

Graphmitter.random = function (nodes, edges) {
  if(isNaN(+nodes)) throw new Error('nodes must be a number')
  if(isNaN(+edges)) throw new Error('edges must be a number')

  var n = 0, g = new Graphmitter()

  function rand(n) {
    return '#'+~~(Math.random()*n)
  }

  for(var i = 0; i < nodes; i++)
    g.node('#'+i)

  for(var i = 0; i < edges; i++) {
    var a = rand(nodes), b = rand(nodes)
    g.edge(a, b).edge(b, a)
  }

  return g
}

//
// Algorithms
//

// probably move these to another file when there get to be lots of them.

proto.traverse = function (opts) {
  opts = opts || {}
  var start = opts.start
  var hops = opts.hops
  var max = opts.max

  if(!start) throw new Error('Graphmitter#traverse: start must be provided')

  var nodes = 1

  var reachable = {}
  var queue

  var queue = [{key: start, hops: 0}]

  reachable[start] = 0

  while(queue.length && (!max || nodes < max)) {
    var o = queue.shift()
    var h = o.hops
    var n = this.nodes[o.key]
    if(n && (!hops || (h + 1 <= hops)))
      n.each(function (k) {
        if(reachable[k] != null) return
        reachable[k] = h + 1
        nodes ++
        queue.push({key: k, hops: h + 1})
      })
  }

  return reachable

}

// page rank. I adapted the algorithm to use
// forward links instead of backward links which means
// we only have to traverse the graph one time.

proto.rank = function (opts) {
  opts = opts || {}

  var ranks = {}, links = {}, _ranks = {}
  var N = count(this.nodes)
  var iterations = opts.iterations || 1
  var damping = opts.damping || 0.85
  var init = (1 - damping) / N

  //initialize
  this.each(function (k, n) {
    ranks[k] = 1/N; _ranks[k] = init
    links[k] = count(n.edges)
  })

  while(iterations --> 0) {

    //iteration
    this.each(function (j, n) {
      var r = damping*(ranks[j]/links[j])
      n.each(function (k) { _ranks[k] += r })
    })

    //reset
    for(var k in ranks)
      ranks[k] = init

    var __ranks = ranks
    ranks = _ranks
    _ranks = __ranks
  }
  return ranks
}

<commit_msg>implement hops events<commit_after>

var EventEmitter = require('events').EventEmitter

var inherits = require('util').inherits

inherits(Graphmitter, EventEmitter)

module.exports = Graphmitter

function each(obj, iter) {
  for(var k in obj) iter(k, obj[k])
}

function count(obj) {
  var c = 0
  for(var k in obj) c++
  return c
}

//
// Node / Vertice
//

function Node () {
  this.edges = {}
}

var nproto = Node.prototype

//returns the old data for this edge..
nproto.edge = function (to, data) {
  var _data = this.edges[to]
  this.edges[to] = (data == null ? true : data)
  return _data
}

//also returns the old data for this edge..
nproto.del = function (to, data) {
  var _data = this.edges[to]
  delete this.edges[to]
  return _data
}

nproto.each = function (iter) {
  each(this.edges, iter)
  return this
}

//
// the whole graph
//

function Graphmitter () {
  if(!(this instanceof Graphmitter)) return new Graphmitter()
  this.nodes = {}
}

var proto = Graphmitter.prototype

proto.hasNode = function (n) {
  return !!this.nodes[n]
}

proto.hasEdge = function (f, t) {
  return this.hasNode(f) && !!this.nodes[f].edges[t] != null
}

proto.node = function (n) {
  return this.nodes[n] = this.nodes[n] || new Node(n)
}

proto.edge = function (from, to, data) {
  data = (data == null ? true : data)
  var f = this.node(from)
  this.node(to)
  var _data = f.edge(to, data)

  if(_data !== data)
    this.emit('edge', from, to, data, _data)
  return this
}

proto.del = function (from, to) {
  var data = this.node(from).del(to)
  if (typeof data !== 'undefined')
    this.emit('del', from, to, data)
  return this
}

proto.each = function (iter) {
  each(this.nodes, iter)
  return this
}

proto.toJSON = function (iter) {
  var g = {}
  this.each(function (k, v) {
    var e = {}
    v.each(function (k, v) {
      e[k] = v
    })
    g[k] = e
  })
  return g
}

//
// graph generators
//

Graphmitter.random = function (nodes, edges) {
  if(isNaN(+nodes)) throw new Error('nodes must be a number')
  if(isNaN(+edges)) throw new Error('edges must be a number')

  var n = 0, g = new Graphmitter()

  function rand(n) {
    return '#'+~~(Math.random()*n)
  }

  for(var i = 0; i < nodes; i++)
    g.node('#'+i)

  for(var i = 0; i < edges; i++) {
    var a = rand(nodes), b = rand(nodes)
    g.edge(a, b).edge(b, a)
  }

  return g
}

//
// Algorithms
//

// probably move these to another file when there get to be lots of them.

proto.traverse = function (opts) {
  opts = opts || {}
  var start = opts.start
  var hops = opts.hops
  var max = opts.max

  if(!start) throw new Error('Graphmitter#traverse: start must be provided')

  var nodes = 1

  var reachable = {}
  var queue

  var queue = [{key: start, hops: 0}]

  reachable[start] = 0

  while(queue.length && (!max || nodes < max)) {
    var o = queue.shift()
    var h = o.hops
    var n = this.nodes[o.key]
    if(n && (!hops || (h + 1 <= hops)))
      n.each(function (k) {
        if(reachable[k] != null) return
        reachable[k] = h + 1
        nodes ++
        queue.push({key: k, hops: h + 1})
      })
  }

  return reachable

}

// page rank. I adapted the algorithm to use
// forward links instead of backward links which means
// we only have to traverse the graph one time.

proto.rank = function (opts) {
  opts = opts || {}

  var ranks = {}, links = {}, _ranks = {}
  var N = count(this.nodes)
  var iterations = opts.iterations || 1
  var damping = opts.damping || 0.85
  var init = (1 - damping) / N

  //initialize
  this.each(function (k, n) {
    ranks[k] = 1/N; _ranks[k] = init
    links[k] = count(n.edges)
  })

  while(iterations --> 0) {

    //iteration
    this.each(function (j, n) {
      var r = damping*(ranks[j]/links[j])
      n.each(function (k) { _ranks[k] += r })
    })

    //reset
    for(var k in ranks)
      ranks[k] = init

    var __ranks = ranks
    ranks = _ranks
    _ranks = __ranks
  }
  return ranks
}

proto.changes = function (opts, listener) {
  var self = this
  var max = opts.hops || 3
  var hops = this.traverse(opts)

  function onEdge (from, to) {
    //if this edge is part of the initial setd
    if(hops[from] != null && hops[from] < max) {
      if(hops[to] == null) {
        var h = hops[from] + 1
        if(hops[to] == null || hops[to] > h)
          listener(from, to, hops[to] = h)
      }
    }
  }

  this.on('edge', onEdge)

  return function () {
    self.removeListener('edge', onEdge)
  }
}
<|endoftext|>