<commit_before>var commandLine = (function(cArgs) {
  var args = {};
  var params = [];

  for(var i=0, arg; arg = cArgs[i]; i++) {
    if( arg.indexOf( "--" ) == 0 ) {
      arg = arg.substring(2).split( "=" );
      args[arg.shift()] = (arg[0] == null) ? true : arg.join( "=" );
    } else {
      params.push(arg);
    }
  }

  return { options: args, params: params };
}(arguments));

function isLocalFile(file) {
  return !(file.match(/^https?:/));
}

function moduleSource() {
  var opts = {
    output: ''
  };

  runCommand('cat', libPath + '/lib/module.js', opts);

  return opts.output + '\n';
}

function isModule(file) {
  return !!file.match(/^((\.|\.\.|[a-zA-Z0-9\-_]+)\/)*[a-zA-Z0-9\-_]+$/);
}

function getModulePath(file) {
  return [require.modulePath, file, '.js'].join('');
}

function bundle(files) {
  return files.map(function(file) {
    var path = isModule(file) ? getModulePath(file) : file;

    var opts = {
      output: ''
    }

    if (isLocalFile(path)) {
      runCommand('cat', path, opts);
    } else {
      runCommand('curl', '-s', path, opts);
    }

    return opts.output + "\nrequire.loaded.push('" + file + "');";
  }).join('\n');
}

var libPath = commandLine.params[0];
var sourceFile = commandLine.params[1];
var destFile = commandLine.params[2];
var minify = !commandLine.options['no-min'];
var bundleModule = commandLine.options['with-modulejs'];
var modulePath = (commandLine.options['modules'] && commandLine.options['modules'].replace(/([^\/])$/, '$1/')) || '';
var docRoot = (commandLine.options['docroot'] && commandLine.options['docroot'].replace(/([^\/])$/, '$1/')) || '';
var cwd = (commandLine.options['cwd'] && commandLine.options['cwd'].replace(/([^\/])$/, '$1/')) || '';

print('Building ' + sourceFile + '...');

load(libPath + '/vendor/env.js');

Envjs({
    scriptTypes : {
        '': true,
        'text/javascript': true
    }
});

load(libPath + '/lib/module.js');

require.modulePath = modulePath;
require.docRoot = docRoot;
require.cwd = cwd;

require(sourceFile, function() {
  var opts;

  var buildOrder = require.loaded.reverse();

  print('Build order: ' + buildOrder.join(', '));

  var source = bundleModule ? moduleSource() : '';
  source += bundle(buildOrder);

  if (minify) {
    opts = {
      input: source,
      output: ''
    };

    runCommand('java', '-jar',
               libPath + '/vendor/yuicompressor-2.4.2.jar',
               '--type', 'js', opts);

    source = opts.output;
  }

  Envjs.writeToFile(source, Envjs.uri(destFile));
  print('Built file to ' + destFile);
});

<commit_msg>added usage instructions and experiment full bundling that doesnt quite work properly<commit_after>var commandLine = (function(cArgs) {
  var args = {};
  var params = [];

  for(var i=0, arg; arg = cArgs[i]; i++) {
    if( arg.indexOf( "--" ) == 0 ) {
      arg = arg.substring(2).split( "=" );
      args[arg.shift()] = (arg[0] == null) ? true : arg.join( "=" );
    } else {
      params.push(arg);
    }
  }

  return { options: args, params: params };
}(arguments));

function isLocalFile(file) {
  return !(file.match(/^https?:/));
}

function moduleSource() {
  var opts = {
    output: ''
  };

  runCommand('cat', libPath + '/lib/module.js', opts);

  return [opts.output, 'require.modulePath = "' + bundleModule + '";'].join('');
}

function isModule(file) {
  return !!file.match(/^((\.|\.\.|[a-zA-Z0-9\-_]+)\/)*[a-zA-Z0-9\-_]+$/);
}

function getModulePath(file) {
  return [require.modulePath, file, '.js'].join('');
}

function bundle(files) {
  return files.map(function(file) {
    var path = isModule(file) ? getModulePath(file) : file;

    var opts = {
      output: ''
    }

    if (isLocalFile(path)) {
      runCommand('cat', path, opts);
    } else {
      runCommand('curl', '-s', path, opts);
    }

    return opts.output + "\nrequire.loaded.push('" + file + "');";
  }).join('\n');
}

var libPath = commandLine.params[0];
var sourceFile = commandLine.params[1];
var destFile = commandLine.params[2];
var minify = !commandLine.options['no-min'];
var bundleModule = (commandLine.options['with-modulejs'] && commandLine.options['with-modulejs'].replace(/([^\/])$/, '$1/'));
var modulePath = (commandLine.options['modules'] && commandLine.options['modules'].replace(/([^\/])$/, '$1/')) || '';
var docRoot = (commandLine.options['docroot'] && commandLine.options['docroot'].replace(/([^\/])$/, '$1/')) || '';
var cwd = (commandLine.options['cwd'] && commandLine.options['cwd'].replace(/([^\/])$/, '$1/')) || '';

if (commandLine.options['help'] || !(sourceFile && destFile)) {
  print('Module.js build tool (c) Dan Webb 2010 licensed under an MIT LICENSE');
  print('Usage: build {options} {source_file} {destination_file}');
  print('       --no-min                      disable minification');
  print('       --with-modulejs=modulePath    embed module.js and (optionally) set module path');
  print('       --modules=path                path to unbuilt modules');
  print('       --docRoot=path                path to / (your sites web root)');
  print('       --cwd=path                    path to current directory if you use relative paths');
  quit();
}

print('Build starting from ' + sourceFile + '...');

load(libPath + '/vendor/env.js');

Envjs({
    scriptTypes : {
        '': true,
        'text/javascript': true
    }
});

load(libPath + '/lib/module.js');

require.modulePath = modulePath;
require.docRoot = docRoot;
require.cwd = cwd;

require(sourceFile, function() {
  var opts;

  var buildOrder = require.loaded.reverse();

  print('Build order:');
  for (var i=0, item; item = buildOrder[i]; i++) {
    print(item);
  }

  var source = bundleModule ? moduleSource() : '';
  source += bundle(buildOrder);

  if (minify) {
    opts = {
      input: source,
      output: ''
    };

    runCommand('java', '-jar',
               libPath + '/vendor/yuicompressor-2.4.2.jar',
               '--type', 'js', opts);

    source = opts.output;
  }

  Envjs.writeToFile(source, Envjs.uri(destFile));
  print('Built file to ' + destFile);
});

<|endoftext|>


<commit_before>'use strict';

const url = require('url');

const _ = require('lodash');
const level = require('level');
const log = require('minilog')('leveldb');
const NpmPublishStream = require('npm-publish-stream');
const objectHash = require('object-hash');
const Promise = require('bluebird');
const sublevel = require('level-sublevel');
const semver = require('semver');
const ttl = require('level-ttl');

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

const SECONDS = 1000;
const MINUTES = 60 * SECONDS;
const HOURS = 60 * MINUTES;
const DAYS = 24 * HOURS;

// instance decorator for aliases cache invalidation
function cull(config, cache) {
  if (!cache) {
    cache = config;
    config = {};
  }

  const refreshRate = _.get(config, 'npm.follower.refreshRate');
  let skimdb = _.get(config, 'npm.skimdb');

  if (skimdb && typeof skimdb === 'string') {
    skimdb = url.parse(skimdb);

    if (skimdb.port) {
      skimdb.port = parseInt(skimdb.port);
    }
    else {
      if (skimdb.protocol === 'https:') {
        skimdb.port = 443;
      }
      else if (skimdb.protocol === 'http:') {
        skimdb.port = 80;
      }
    }

    // THIS LIBRARY IS DUMB and TAKES PROTOCOL IN A WEIRD FORMAT
    skimdb.protocol += '//';
  }
  else {
    skimdb = {};
  }

  if (cache.name !== 'aliases') {
    throw new Error('This decorator should only be used for aliases');
  }

  const db = cache.db;
  const pubStream = new NpmPublishStream({
    refreshRate: refreshRate,
    hostname: skimdb.hostname,
    protocol: skimdb.protocol,
    port: skimdb.port
  });

  cache.createKeyStream = function(prefix) {
    // concat-stream -- concat-strean
    const start = prefix;
    const end = start.slice(0, prefix.length - 1)
              + String.fromCharCode(prefix.charCodeAt(prefix.length - 1) + 1);

    return this.db.createKeyStream({ start: start, end: end });
  };

  cache._destroy = cache.destroy;

  cache.destroy = function() {
    this._pubStream.destroy();
    return cache._destroy();
  };

  cache._cullHandler = function(data) {
    // I just happen to know that the name (should, anyway) already has the
    // format of the key prior to the '@'.
    const prefix = data.doc.name;

    const dist_tags = data.doc['dist-tags'];

    // Should invalidate anything that has a dist-tag
    // TODO: Mad overkill for any project with tons of dist-tags, any way to
    // figure out what the update was?
    Object.keys(dist_tags).forEach((tag) => {
      const module_version = dist_tags[tag];

      const stream = this.createKeyStream(prefix);

      stream.on('data', (key) => {
        log.info('cull stream on data', key);

        // Grab the semver range used in the key
        const module_semver = key.split('@').pop();

        // If this alias has been invalidated by the tagged version...
        if (semver.satisfies(module_version, module_semver) || module_semver == tag) {
          log.info(`culling \`${key}\`...`);
          this._del(key);
        }
      });
    });
  };
  pubStream.on('data', (data) => cache._cullHandler(data));

  pubStream.on('error', (err) => {
    log.info('pubstream error:', err.message);
    log.info(err.stack);
  });

  cache._pubStream = pubStream;

  return cache;
}

class Cache {
  constructor(name, db, opts) {
    opts = opts || {}
    this.db = db.sublevel(name);
    this.name = name;
    this.hashfxn = opts.hashfxn || opts.hashFunction || defaultHashFxn
    this.ttl = opts.ttl;
  }

  destroy() {
    return Promise.fromCallback((cb) => this.db.close(cb));
  }

  _get(key) {
    return Promise.fromCallback((cb) => this.db.get(key, cb))
      .then((v) => {
        try {
          return JSON.parse(v)
        }
        catch (parseError) {
          parseError.name = 'JSONParseError';
          throw parseError;
        }
      });
  }

  get(key) {
    return Promise.try(() => {
      return this._get(this.hashfxn(key));
    });
  }

  _put(key, val, opts) {
    return Promise.try(() => {
      return Promise.fromCallback((cb) => this.db.put(key, JSON.stringify(val), opts, cb));
    });
  }

  put(key, val, opts) {
    return Promise.try(() => {
      return this._put(this.hashfxn(key), val, opts);
    });
  }

  _del(key) {
    return Promise.try(() => {
      return Promise.fromCallback((cb) => this.db.del(key, cb));
    });
  }

  del(key) {
    return Promise.try(() => {
      return this._del(this.hashfxn(key));
    });
  }

  check(body, generate) {
    const hash = this.hashfxn(body);
    const db = this.db;
    const name = this.name;
    const ttl = this.ttl;

    log.info(`cache: checking \`${name}\` for hash \`${hash}\`...`);

    return this._get(hash)
      .catch((err) => {
        if (err.name !== 'JSONParseError') {
          throw err;
        }
        // Holy shit, something is fucking up the db
        // Try to del the thing and then get
        return this._del(hash).then(() => {
          // Being a little bit of an asshole here
          err.name = 'NotFoundError';
          throw err;
        });
      }).catch((err) => {

        if (err.name !== 'NotFoundError') {
          throw err;
        }

        log.info(`cache: \`${name}\` did not have \`${hash}\`.`);
        return Promise.try(() => {
          return generate();
        }).then((res) => {
          if (!res) {
            const err = new Error(`Acceptable version not found for \`${name}\` with hash \`${hash}\``);
            err.name = 'NotFoundError'
            throw err;
          }

          log.info(
            `cache: saving hash \`${hash}\` in \`${name}\` ${(
              (typeof ttl === 'number')
                ? `with ttl ${ttl}...`
                : '...'
            )}`
          );

          if (ttl) {
            return this._put(hash, res, { ttl: ttl }).then(finish);
          }
          return this._put(hash, res).then(finish);

          function finish() {
            log.info(`saved hash \`${hash}\` in \`${name}\`.`);
            return res;
          }
        });
      })
    ;
  };
}

function defaultHashFxn(o) {
  return objectHash(o);
}

function aliasesHashFxn(input) {
  input = validate.validateInput(input);

  const scope = input.module_scope;
  const name = input.module_name;
  const semver = input.module_semver;
  const subfile = input.module_subfile;

  return `${[ scope, name, subfile ].filter((b) => b).join('/')}@${semver}`;

}

function multibundlesHashFxn(input) {
  if (typeof input === 'string' && input.length === 32) {
    log.info('cache: Input for `multibundles` appears to be an md5 hash already');
    return input;
  }
  return defaultHashFxn(input);
};

function statusesHashFxn(input) {
  input = validate.validateInput(input);

  const scope = input.module_scope;
  const name = input.module_name;
  const version = input.module_version;
  const subfile = input.module_subfile;

  return `${[ scope, name, subfile ].filter((b) => b).join('/')}@${version}`;
};

function createCaches(config) {

  const db = sublevel(ttl(level(_.get(config, 'level.db'))));

  const bundles = new Cache('bundles', db, {
    ttl: 14 * DAYS
  });

  const aliases = cull(config, new Cache('aliases', db, {
    hashfxn: aliasesHashFxn,
    ttl: 1 * DAYS
  }));

  const multibundles = new Cache('multibundles', db, {
    hashfxn: multibundlesHashFxn,
    ttl: 30 * DAYS
  });

  const statuses = new Cache('statuses', db, {
    hashfxn: statusesHashFxn,
    ttl: 30 * DAYS
  });

  return {
    _db: db,
    _destroy: function() {
      return Promise.all([
        bundles.destroy(),
        multibundles.destroy(),
        aliases.destroy(),
        statuses.destroy()
      ]);
    },
    bundles: bundles,
    multibundles: multibundles,
    aliases: aliases,
    statuses: statuses
  };
};

module.exports = {
  cull: cull,
  Cache: Cache,
  createCaches: createCaches,
  defaultHashFxn: defaultHashFxn,
  aliasesHashFxn: aliasesHashFxn,
  multibundlesHashFxn: multibundlesHashFxn,
  statusesHashFxn: statusesHashFxn
};

<commit_msg>Clean up some promise code<commit_after>'use strict';

const url = require('url');

const _ = require('lodash');
const level = require('level');
const log = require('minilog')('leveldb');
const NpmPublishStream = require('npm-publish-stream');
const objectHash = require('object-hash');
const Promise = require('bluebird');
const sublevel = require('level-sublevel');
const semver = require('semver');
const ttl = require('level-ttl');

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

const SECONDS = 1000;
const MINUTES = 60 * SECONDS;
const HOURS = 60 * MINUTES;
const DAYS = 24 * HOURS;

// instance decorator for aliases cache invalidation
function cull(config, cache) {
  if (!cache) {
    cache = config;
    config = {};
  }

  const refreshRate = _.get(config, 'npm.follower.refreshRate');
  let skimdb = _.get(config, 'npm.skimdb');

  if (skimdb && typeof skimdb === 'string') {
    skimdb = url.parse(skimdb);

    if (skimdb.port) {
      skimdb.port = parseInt(skimdb.port);
    }
    else {
      if (skimdb.protocol === 'https:') {
        skimdb.port = 443;
      }
      else if (skimdb.protocol === 'http:') {
        skimdb.port = 80;
      }
    }

    // THIS LIBRARY IS DUMB and TAKES PROTOCOL IN A WEIRD FORMAT
    skimdb.protocol += '//';
  }
  else {
    skimdb = {};
  }

  if (cache.name !== 'aliases') {
    throw new Error('This decorator should only be used for aliases');
  }

  const db = cache.db;
  const pubStream = new NpmPublishStream({
    refreshRate: refreshRate,
    hostname: skimdb.hostname,
    protocol: skimdb.protocol,
    port: skimdb.port
  });

  cache.createKeyStream = function(prefix) {
    // concat-stream -- concat-strean
    const start = prefix;
    const end = start.slice(0, prefix.length - 1)
              + String.fromCharCode(prefix.charCodeAt(prefix.length - 1) + 1);

    return this.db.createKeyStream({ start: start, end: end });
  };

  cache._destroy = cache.destroy;

  cache.destroy = function() {
    this._pubStream.destroy();
    return cache._destroy();
  };

  cache._cullHandler = function(data) {
    // I just happen to know that the name (should, anyway) already has the
    // format of the key prior to the '@'.
    const prefix = data.doc.name;

    const dist_tags = data.doc['dist-tags'];

    // Should invalidate anything that has a dist-tag
    // TODO: Mad overkill for any project with tons of dist-tags, any way to
    // figure out what the update was?
    Object.keys(dist_tags).forEach((tag) => {
      const module_version = dist_tags[tag];

      const stream = this.createKeyStream(prefix);

      stream.on('data', (key) => {
        log.info('cull stream on data', key);

        // Grab the semver range used in the key
        const module_semver = key.split('@').pop();

        // If this alias has been invalidated by the tagged version...
        if (semver.satisfies(module_version, module_semver) || module_semver == tag) {
          log.info(`culling \`${key}\`...`);
          this._del(key);
        }
      });
    });
  };
  pubStream.on('data', (data) => cache._cullHandler(data));

  pubStream.on('error', (err) => {
    log.info('pubstream error:', err.message);
    log.info(err.stack);
  });

  cache._pubStream = pubStream;

  return cache;
}

class Cache {
  constructor(name, db, opts) {
    opts = opts || {}
    this.db = db.sublevel(name);
    this.name = name;
    this.hashfxn = opts.hashfxn || opts.hashFunction || defaultHashFxn
    this.ttl = opts.ttl;
  }

  destroy() {
    return Promise.fromCallback((cb) => this.db.close(cb));
  }

  _get(key) {
    return Promise.fromCallback((cb) => this.db.get(key, cb))
      .then((v) => {
        try {
          return JSON.parse(v)
        }
        catch (parseError) {
          parseError.name = 'JSONParseError';
          throw parseError;
        }
      });
  }

  get(key) {
    return Promise.try(() => {
      return this._get(this.hashfxn(key));
    });
  }

  _put(key, val, opts) {
    return Promise.fromCallback((cb) => this.db.put(key, JSON.stringify(val), opts, cb));
  }

  put(key, val, opts) {
    return Promise.try(() => {
      return this._put(this.hashfxn(key), val, opts);
    });
  }

  _del(key) {
    return Promise.fromCallback((cb) => this.db.del(key, cb));
  }

  del(key) {
    return Promise.try(() => {
      return this._del(this.hashfxn(key));
    });
  }

  check(body, generate) {
    const hash = this.hashfxn(body);
    const db = this.db;
    const name = this.name;
    const ttl = this.ttl;

    log.info(`cache: checking \`${name}\` for hash \`${hash}\`...`);

    return this._get(hash)
      .catch((err) => {
        if (err.name !== 'JSONParseError') {
          throw err;
        }
        // Holy shit, something is fucking up the db
        // Try to del the thing and then get
        return this._del(hash).then(() => {
          // Being a little bit of an asshole here
          err.name = 'NotFoundError';
          throw err;
        });
      }).catch((err) => {

        if (err.name !== 'NotFoundError') {
          throw err;
        }

        log.info(`cache: \`${name}\` did not have \`${hash}\`.`);
        return Promise.try(generate).then((res) => {
          if (!res) {
            const err = new Error(`Acceptable version not found for \`${name}\` with hash \`${hash}\``);
            err.name = 'NotFoundError'
            throw err;
          }

          log.info(
            `cache: saving hash \`${hash}\` in \`${name}\` ${(
              (typeof ttl === 'number')
                ? `with ttl ${ttl}...`
                : '...'
            )}`
          );

          if (ttl) {
            return this._put(hash, res, { ttl: ttl }).then(finish);
          }
          return this._put(hash, res).then(finish);

          function finish() {
            log.info(`saved hash \`${hash}\` in \`${name}\`.`);
            return res;
          }
        });
      })
    ;
  };
}

function defaultHashFxn(o) {
  return objectHash(o);
}

function aliasesHashFxn(input) {
  input = validate.validateInput(input);

  const scope = input.module_scope;
  const name = input.module_name;
  const semver = input.module_semver;
  const subfile = input.module_subfile;

  return `${[ scope, name, subfile ].filter((b) => b).join('/')}@${semver}`;

}

function multibundlesHashFxn(input) {
  if (typeof input === 'string' && input.length === 32) {
    log.info('cache: Input for `multibundles` appears to be an md5 hash already');
    return input;
  }
  return defaultHashFxn(input);
};

function statusesHashFxn(input) {
  input = validate.validateInput(input);

  const scope = input.module_scope;
  const name = input.module_name;
  const version = input.module_version;
  const subfile = input.module_subfile;

  return `${[ scope, name, subfile ].filter((b) => b).join('/')}@${version}`;
};

function createCaches(config) {

  const db = sublevel(ttl(level(_.get(config, 'level.db'))));

  const bundles = new Cache('bundles', db, {
    ttl: 14 * DAYS
  });

  const aliases = cull(config, new Cache('aliases', db, {
    hashfxn: aliasesHashFxn,
    ttl: 1 * DAYS
  }));

  const multibundles = new Cache('multibundles', db, {
    hashfxn: multibundlesHashFxn,
    ttl: 30 * DAYS
  });

  const statuses = new Cache('statuses', db, {
    hashfxn: statusesHashFxn,
    ttl: 30 * DAYS
  });

  return {
    _db: db,
    _destroy: function() {
      return Promise.all([
        bundles.destroy(),
        multibundles.destroy(),
        aliases.destroy(),
        statuses.destroy()
      ]);
    },
    bundles: bundles,
    multibundles: multibundles,
    aliases: aliases,
    statuses: statuses
  };
};

module.exports = {
  cull: cull,
  Cache: Cache,
  createCaches: createCaches,
  defaultHashFxn: defaultHashFxn,
  aliasesHashFxn: aliasesHashFxn,
  multibundlesHashFxn: multibundlesHashFxn,
  statusesHashFxn: statusesHashFxn
};

<|endoftext|>


<commit_before>
const _        = require('ramda');
const Bluebird = require('bluebird');
const v        = require('validator');

// PASS : Nothing
const PASS = _.always(null);

// FAIL : String -> (* -> String)
const FAIL = (msg) => _.always(msg);

const MSG  = {
  REQUIRED           : 'required'
, REQUIRE_ARRAY      : 'require_array'
, CANNOT_BE_EQUAL    : 'cannot_be_equal'
, NOT_INT            : 'not_int'
, NOT_STRING         : 'not_string'
, NOT_FUNCTION       : 'not_function'
, NOT_ARRAY          : 'not_array'
, LIST_ITEM_NOT_SPEC : 'list_item_not_spec'
, NOT_NULLABLE       : 'cannot_be_null'
, IMMUTABLE          : 'cannot_change_immutable'
, NOT_IN_RANGE       : 'not_in_range'
, NOT_IN             : 'not_in'
, NOT_BOOL           : 'not_bool'
};

// basic : (a -> Bool), String -> (a -> Maybe String)
const basic = (test, msg) => _.ifElse(test, PASS, FAIL(msg));

// extern : (a -> Bool), String -> (a -> Maybe String)
const extern = (test, msg) => _.composeP(
  basic(_.equals(true), msg)
, _.when(_.isNil, _.T)
, _.compose(Bluebird.resolve, test)
);

// required : a -> Maybe String
const required = basic(
  _.compose(_.not, _.either(_.isEmpty, _.isNil))
, MSG.REQUIRED
);
required.always_run = true;

// requireArray : a -> Maybe String
const requireArray = basic(_.is(Array), MSG.REQUIRE_ARRAY);
requireArray.always_run = true;

// notEqual : a -> Maybe String
const notEqual = (x) => basic(
  _.compose(_.not, _.equals(x))
, MSG.CANNOT_BE_EQUAL
);

// isInt : a -> Maybe String
const isInt = basic(_.unary(v.isInt), MSG.NOT_INT);

// isString : a -> Maybe String
const isString = basic(_.is(String), MSG.NOT_STRING);

// isFunction : a -> Maybe String
const isFunction = basic(_.is(Function), MSG.NOT_FUNCTION);

// isArray : a -> Maybe String
const isArray = basic(_.is(Array), MSG.NOT_ARRAY);

// listOf : (a -> Bool), String -> Array a -> Promise Maybe String
const listOf  = (check, msg) => (list) => {

  const validator = extern(check, msg || MSG.LIST_ITEM_NOT_SPEC);
  const reducer   = (result, item) => result ? result : validator(item);

  return Bluebird.reduce(list, reducer, null);
};

// notNullable : a -> Maybe String
const notNullable = basic(
  (value, request, current, key) => { return _.all(
    _.compose(_.not, _.equals(null))
    , [ value, _.prop(key, current) ]
    )
  }
  , MSG.NOT_NULLABLE
);

// immutable : a -> Maybe String
const immutable = basic(
  (value, request, current, key) => { return _.propEq(key, value, current) }
  , MSG.IMMUTABLE
);

// _satisfies : Function -> [ String ] -> Bool
const _satisfies = (condFn) => (keys) => _.compose(
  _.not
  , condFn(_.compose(_.not, _.isNil)),
  _.props(keys)
)

// reqIf : Function -> [ String ] -> Maybe String
const _reqIf = (condFn) => (keys) => basic(
  _.useWith(_.or, [ _.identity , _satisfies(condFn)(keys) ])
  , MSG.REQUIRED
)

// reqIfHasAny : [ String ] -> a , Request -> Maybe String
const reqIfHasAny = _reqIf(_.any);

// reqIfHasAll : [ String ] -> a , Request -> Maybe String
const reqIfHasAll = _reqIf(_.all);

// notReqIfHasAny : [ String ] -> a , Request -> Maybe String
const notReqIfHasAny = _reqIf(_.curryN(2, _.compose(_.not, _.any)))

// notReqIfHasAll : [ String ] -> a , Request -> Maybe String
const notReqIfHasAll = _reqIf(_.curryN(2, _.compose(_.not, _.all)))

// _isInRangeInc : Int, Int -> (Int -> Bool)
const _isBetweenInc = (min, max) => _.allPass([ _.lte(min), _.gte(max) ])

// _isBetween : Int -> Maybe String
const _betweenIf = _.ifElse(
  _.identity
, _.always(null)
, _.always(MSG.NOT_IN_RANGE)
);

// isLength : Int, Int -> (a -> Maybe String)
const isLength = (min, max) => _.compose(
  _betweenIf
, _isBetweenInc(min, max)
, _.length
)

// isIn :: Array -> * -> Maybe String
const isIn = (options) => _.ifElse(
  _.flip(_.contains)(options)
, _.always(null)
, _.always(MSG.NOT_IN)
)

// isBool :: Integer -> Maybe String
const isBool = _.ifElse(
 _.either(
  _.flip(_.contains)([true, false]),
  _.compose(
    _.flip(_.contains)([0,1]),
    (i) => parseInt(i, 10)
  )
 )
, _.always(null)
, _.always(MSG.NOT_BOOL)
)

// isInRangeInclusive :: Integer, Integer -> value -> Maybe String
const isInRangeInclusive = (min, max) => _.ifElse(
  _.compose(
    _isBetweenInc(min, max)
  , (i) => parseInt(i,10)
  )
, _.always(null)
, _.always(MSG.NOT_IN_RANGE)
)

// defined :: a -> Maybe String
const defined = _.ifElse(
  _.equals(undefined)
, _.always(MSG.REQUIRED)
, _.always(null)
)

module.exports = {
  MSG
, extern
, required
, requireArray
, notEqual
, isInt
, isString
, isFunction
, isArray
, listOf
, notNullable
, immutable
, reqIfHasAny
, reqIfHasAll
, notReqIfHasAny
, notReqIfHasAll
, isLength
, isIn
, isBool
, isInRangeInclusive
, defined
};
<commit_msg>PR feedback<commit_after>
const _        = require('ramda');
const Bluebird = require('bluebird');
const v        = require('validator');

// PASS : Nothing
const PASS = _.always(null);

// FAIL : String -> (* -> String)
const FAIL = (msg) => _.always(msg);

const MSG  = {
  REQUIRED           : 'required'
, REQUIRE_ARRAY      : 'require_array'
, CANNOT_BE_EQUAL    : 'cannot_be_equal'
, NOT_INT            : 'not_int'
, NOT_STRING         : 'not_string'
, NOT_FUNCTION       : 'not_function'
, NOT_ARRAY          : 'not_array'
, LIST_ITEM_NOT_SPEC : 'list_item_not_spec'
, NOT_NULLABLE       : 'cannot_be_null'
, IMMUTABLE          : 'cannot_change_immutable'
, NOT_IN_RANGE       : 'not_in_range'
, NOT_IN             : 'not_in'
, NOT_BOOL           : 'not_bool'
};

// basic : (a -> Bool), String -> (a -> Maybe String)
const basic = (test, msg) => _.ifElse(test, PASS, FAIL(msg));

// extern : (a -> Bool), String -> (a -> Maybe String)
const extern = (test, msg) => _.composeP(
  basic(_.equals(true), msg)
, _.when(_.isNil, _.T)
, _.compose(Bluebird.resolve, test)
);

// required : a -> Maybe String
const required = basic(
  _.compose(_.not, _.either(_.isEmpty, _.isNil))
, MSG.REQUIRED
);
required.always_run = true;

// requireArray : a -> Maybe String
const requireArray = basic(_.is(Array), MSG.REQUIRE_ARRAY);
requireArray.always_run = true;

// notEqual : a -> Maybe String
const notEqual = (x) => basic(
  _.compose(_.not, _.equals(x))
, MSG.CANNOT_BE_EQUAL
);

// isInt : a -> Maybe String
const isInt = basic(_.unary(v.isInt), MSG.NOT_INT);

// isString : a -> Maybe String
const isString = basic(_.is(String), MSG.NOT_STRING);

// isFunction : a -> Maybe String
const isFunction = basic(_.is(Function), MSG.NOT_FUNCTION);

// isArray : a -> Maybe String
const isArray = basic(_.is(Array), MSG.NOT_ARRAY);

// listOf : (a -> Bool), String -> Array a -> Promise Maybe String
const listOf  = (check, msg) => (list) => {

  const validator = extern(check, msg || MSG.LIST_ITEM_NOT_SPEC);
  const reducer   = (result, item) => result ? result : validator(item);

  return Bluebird.reduce(list, reducer, null);
};

// notNullable : a -> Maybe String
const notNullable = basic(
  (value, request, current, key) => { return _.all(
    _.compose(_.not, _.equals(null))
    , [ value, _.prop(key, current) ]
    )
  }
  , MSG.NOT_NULLABLE
);

// immutable : a -> Maybe String
const immutable = basic(
  (value, request, current, key) => { return _.propEq(key, value, current) }
  , MSG.IMMUTABLE
);

// _satisfies : Function -> [ String ] -> Bool
const _satisfies = (condFn) => (keys) => _.compose(
  _.not
  , condFn(_.compose(_.not, _.isNil)),
  _.props(keys)
)

// reqIf : Function -> [ String ] -> Maybe String
const _reqIf = (condFn) => (keys) => basic(
  _.useWith(_.or, [ _.identity , _satisfies(condFn)(keys) ])
  , MSG.REQUIRED
)

// reqIfHasAny : [ String ] -> a , Request -> Maybe String
const reqIfHasAny = _reqIf(_.any);

// reqIfHasAll : [ String ] -> a , Request -> Maybe String
const reqIfHasAll = _reqIf(_.all);

// notReqIfHasAny : [ String ] -> a , Request -> Maybe String
const notReqIfHasAny = _reqIf(_.curryN(2, _.compose(_.not, _.any)))

// notReqIfHasAll : [ String ] -> a , Request -> Maybe String
const notReqIfHasAll = _reqIf(_.curryN(2, _.compose(_.not, _.all)))

// _isInRangeInc : Int, Int -> (Int -> Bool)
const _isBetweenInc = (min, max) => _.allPass([ _.lte(min), _.gte(max) ])

// _isBetween : Int -> Maybe String
const _betweenIf = _.ifElse(
  _.identity
, _.always(null)
, _.always(MSG.NOT_IN_RANGE)
);

// isLength : Int, Int -> (a -> Maybe String)
const isLength = (min, max) => _.compose(
  _betweenIf
, _isBetweenInc(min, max)
, _.length
)

// isIn :: Array -> * -> Maybe String
const isIn = (options) => _.ifElse(
  _.flip(_.contains)(options)
, _.always(null)
, _.always(MSG.NOT_IN)
)

// isBool :: Integer -> Maybe String
const isBool = _.ifElse(
 _.either(
  _.flip(_.contains)([true, false]),
  _.compose(
    _.flip(_.contains)([0,1]),
    (i) => parseInt(i, 10)
  )
 )
, _.always(null)
, _.always(MSG.NOT_BOOL)
)

// isInRangeInclusive :: Integer, Integer -> value -> Maybe String
const isInRangeInclusive = (min, max) => _.ifElse(
  _.compose(
    _isBetweenInc(min, max)
  , (i) => parseInt(i,10)
  )
, _.always(null)
, _.always(MSG.NOT_IN_RANGE)
)

// is_not = a -> (a -> Bool)
const isNot = (val) => _.compose(_.not, _.equals(val))

// defined = a -> Maybe String
const defined = basic( isNot(undefined), MSG.REQUIRED)

module.exports = {
  MSG
, extern
, required
, requireArray
, notEqual
, isInt
, isString
, isFunction
, isArray
, listOf
, notNullable
, immutable
, reqIfHasAny
, reqIfHasAll
, notReqIfHasAny
, notReqIfHasAll
, isLength
, isIn
, isBool
, isInRangeInclusive
, defined
};
<|endoftext|>


<commit_before>var QUnit = require('../support/qunit/qunit/qunit.js'),
    path = require('path'),
    _ = require('underscore'),
    trace = require('tracejs').trace;

// cycle.js: This file contains two functions, JSON.decycle and JSON.retrocycle,
// which make it possible to encode cyclical structures and dags in JSON, and to
// then recover them. JSONPath is used to represent the links.
// http://GOESSNER.net/articles/JsonPath/
require('../support/json/cycle');

var options = JSON.parse(process.argv[2]),
    currentModule = path.basename(options.code.path, '.js'),
    currentTest;

process.on('uncaughtException', function(e) {
    QUnit.ok(false, 'Test threw unexpected exception: ' + e);
    QUnit.start();
});

QUnit.config.autorun = false;
QUnit.config.autostart = false;

// make qunit api global, like it is in the browser
_.extend(global, QUnit);

// as well as the QUnit variable itself
global.QUnit = QUnit;

/**
 * Require a resource.
 * @param {Object} res
 */
function _require(res, addToGlobal) {
    var exports = require(res.path.replace(/\.js$/, ''));

    if (addToGlobal) {
        // resource can define 'namespace' to expose its exports as a named object
        if (res.namespace) {
            global[res.namespace] = exports;
        } else {
            _.extend(global, exports);
        }
    }

    QUnit.start();
}

/**
 * Calculate coverage stats using bunker
 */
function calcCoverage() {

}

/**
 * Callback for each started test.
 * @param {Object} test
 */
QUnit.testStart(function(test) {
    // currentTest is undefined while first test is not done yet
    currentTest = test.name;

    // use last module name if no module name defined
    currentModule = test.module || currentModule;
});

/**
 * Callback for each assertion.
 * @param {Object} data
 */
QUnit.log(function(data) {
    data.test = this.config.current.testName;
    data.module = currentModule;
    process.send({
        event: 'assertionDone',
        data: JSON.decycle(data)
    });
});

/**
 * Callback for one done test.
 * @param {Object} test
 */
QUnit.testDone(function(data) {
    // use last module name if no module name defined
    data.module = data.module || currentModule;
    process.send({
        event: 'testDone',
        data: data
    });
});

/**
 * Callback for all done tests in the file.
 * @param {Object} res
 */
QUnit.done(_.debounce(function(data) {
    if (options.coverage) {
        data.coverage = calcCoverage();
    }

    process.send({
        event: 'done',
        data: data
    });
}, 1000));

/**
 * Provide better stack traces
 */
var error = console.error;
console.error = function(obj) {
    // log full stacktrace
    if (obj && obj.stack) {
        obj = trace(obj);
    }

    return error.apply(this, arguments);
};

// require deps
options.deps.forEach(_require, true);

// require code
_require(options.code, true);

// require tests
options.tests.forEach(function(res) {
    _require(res, false);
});
<commit_msg>closes #63 forward an uncaughtException if no tests are running<commit_after>var QUnit = require('../support/qunit/qunit/qunit.js'),
    path = require('path'),
    _ = require('underscore'),
    trace = require('tracejs').trace;

// cycle.js: This file contains two functions, JSON.decycle and JSON.retrocycle,
// which make it possible to encode cyclical structures and dags in JSON, and to
// then recover them. JSONPath is used to represent the links.
// http://GOESSNER.net/articles/JsonPath/
require('../support/json/cycle');

var options = JSON.parse(process.argv[2]),
    currentModule = path.basename(options.code.path, '.js'),
    currentTest;

process.on('uncaughtException', function(err) {
    if (QUnit.config.current) {
        QUnit.ok(false, 'Test threw unexpected exception: ' + err);
        QUnit.start();
    } else {
        throw err;
    }
});

QUnit.config.autorun = false;
QUnit.config.autostart = false;

// make qunit api global, like it is in the browser
_.extend(global, QUnit);

// as well as the QUnit variable itself
global.QUnit = QUnit;

/**
 * Require a resource.
 * @param {Object} res
 */
function _require(res, addToGlobal) {
    var exports = require(res.path.replace(/\.js$/, ''));

    if (addToGlobal) {
        // resource can define 'namespace' to expose its exports as a named object
        if (res.namespace) {
            global[res.namespace] = exports;
        } else {
            _.extend(global, exports);
        }
    }

    QUnit.start();
}

/**
 * Calculate coverage stats using bunker
 */
function calcCoverage() {

}

/**
 * Callback for each started test.
 * @param {Object} test
 */
QUnit.testStart(function(test) {
    // currentTest is undefined while first test is not done yet
    currentTest = test.name;

    // use last module name if no module name defined
    currentModule = test.module || currentModule;
});

/**
 * Callback for each assertion.
 * @param {Object} data
 */
QUnit.log(function(data) {
    data.test = this.config.current.testName;
    data.module = currentModule;
    process.send({
        event: 'assertionDone',
        data: JSON.decycle(data)
    });
});

/**
 * Callback for one done test.
 * @param {Object} test
 */
QUnit.testDone(function(data) {
    // use last module name if no module name defined
    data.module = data.module || currentModule;
    process.send({
        event: 'testDone',
        data: data
    });
});

/**
 * Callback for all done tests in the file.
 * @param {Object} res
 */
QUnit.done(_.debounce(function(data) {
    if (options.coverage) {
        data.coverage = calcCoverage();
    }

    process.send({
        event: 'done',
        data: data
    });
}, 1000));

/**
 * Provide better stack traces
 */
var error = console.error;
console.error = function(obj) {
    // log full stacktrace
    if (obj && obj.stack) {
        obj = trace(obj);
    }

    return error.apply(this, arguments);
};

// require deps
options.deps.forEach(_require, true);

// require code
_require(options.code, true);

// require tests
options.tests.forEach(function(res) {
    _require(res, false);
});
<|endoftext|>


<commit_before>const db = require('./db')

[]_asdsad

function cache ({fn, minutes=5, log=''}) {
  this.expires = new Date(new Date().getTime() + minutes *  60 * 1000)
  this.result = false
  this.resolve = () => {
    if (this.result && this.expires > new Date().getTime()) {
      return Promise.resolve(true)
        .then(() => this.result)
    } else {
      return Promise.resolve(true)
        .then(() => fn())
        .then(res => {
          this.result = res
          this.expires = new Date(new Date().getTime() + minutes * 60000)
          return res
        })
        .then(res => {
          if (log === 'Index') {
            try {
              db.saveApiResponse(res)
            } catch (e) {
              console.log('Error saving to history database')
            }
          }
          return res
        })
    }
  }
  return this
}

module.exports = (obj) => new cache(obj)<commit_msg>wrong linting<commit_after>const db = require('./db')

function cache ({fn, minutes=5, log=''}) {
  this.expires = new Date(new Date().getTime() + minutes *  60 * 1000)
  this.result = false
  this.resolve = () => {
    if (this.result && this.expires > new Date().getTime()) {
    return Promise.resolve(true)
        .then(() => this.result)
    } else {
    return Promise.resolve(true)
        .then(() => fn())
        .then(res => {
          this.result = res
          this.expires = new Date(new Date().getTime() + minutes * 60000)
          return res
        })
        .then(res => {
          if (log === 'Index') {
            try {
              db.saveApiResponse(res)
            } catch (e) {
              console.log('Error saving to history database')
            }
          }
          return res
        })
    }
  }
  return this
}

module.exports = (obj) => new cache(obj)<|endoftext|>


<commit_before>/**

  cache.js

  Basic caching for server. Entries consist of:
  • key:   cache key for reference
  • ttl:   cache time to live
  • type:  file type (for sending)
  • val:   value

  Middleware creates a cache key based on the route and returns the entry if available.

**/

var get = require("lodash/get");
var Utils = require("./utils");
var minimatch = require("minimatch");

var Cache = module.exports = {

  store: require("lru-cache")({

    max: get(global.shipp, "config.cache.sizeLimit"),

    length: function(cached, key) {

      var val = cached.val;

      // Assume vanilla types have byte size 4, calculate all others
      var size = 4;
      if (Buffer.isBuffer(val))
        size = val.length;
      else if ("string" === typeof(val))
        size = Buffer.byteLength(val);

      // Object keys = 10 (string length of ttl, type, val), ttl = 4. Total = 14
      return size + 17 + Buffer.byteLength(key) + Buffer.byteLength(cached.type || "");

    }

  }),

  has: function(key) {
    return Cache.store.has(key);
  },

  length: function() {
    return Cache.store.length;
  },

  /**

    Gets an object from the cache.

    @param {String} key The key of the object
    @returns {*} The object

  **/

  get: function(key) {
    return Cache.store.get(key);
  },

  /**

    Sets an object in the cache.

    @param {String} key The key of the object
    @param {Object} value The object to store

  **/

  set: function(key, value) {
    if ("undefined" === typeof value)
      Cache.unset(key);
    else
      Cache.store.set(key, value);
  },

  /**

    Unsets an object in the cache.

    @param {String} key The key of the object

  **/

  unset: function(key) {
    Cache.store.del(key);
  },

  /**

    Invalidates objects in the cache via glob matching.

    @param {String} pattern Glob pattern (see https://github.com/isaacs/minimatch)
    @param {Boolean} [ignoreCase] When true, performs case insensitive match (default: true)

    @returns {Array} Array of strings invalidated

  **/

  invalidate: function(pattern, ignoreCase) {

    var keys = Cache.store.keys(),
        matches = [],
        re;

    ignoreCase = (false === ignoreCase) ? false : true;

    // minimatch.match seems to have issues with matching slashes. Use its RegExp instead.
    // We prefix gzip, etc. in order to allow the user to think of patterns in terms of "routes".
    if ("string" === typeof pattern)
      re = minimatch.makeRe("?(gzip:|identity:|deflate:)" + pattern, { nocase: ignoreCase });
    else
      re = pattern;

    if (re instanceof RegExp)
      matches = keys.filter(function(key) {
        return re.test(key);
      });
    else
      throw new Error("Pattern must be string (glob) or RegExp");

    // Delete from cache
    matches.forEach(Cache.unset);
    global.shipp.debug("Invalidated " + matches.length + " route(s) given " + pattern);

    return matches;

  },

  /**

    Clear the cache entirely, throwing away all values.

  **/

  reset: function() {
    return Cache.store.reset();
  },

  /**

    Return an array of the keys in the cache.

  **/

  keys: function() {
    return Cache.store.keys();
  },

  /**

    Middleware

    Note: we do not currently use query strings as part of caching. To do this effectively,
    certain parameters (such as those used in web tracking) must be removed.

  **/

  middleware: function() {

    var router = global.shipp.router();

    router.use("*", function(req, res, next) {

      var compression = req.acceptsEncodings(["gzip", "deflate", "identity"]) || "identity";

      // Attach cacheKey to the request
      var cacheKey = res.locals.cacheKey = compression + ":" + req.originalUrl;

      if (Cache.has(cacheKey)) {
        var cached = Cache.get(cacheKey);
        Utils.send(res, cached.type, cached.val, compression, cached.ttl);
      } else {
        next();
      }

    })

    global.shipp.debug("Caching middleware added");

    return router;

  }

};
<commit_msg>Cache: adds debugging<commit_after>/**

  cache.js

  Basic caching for server. Entries consist of:
  • key:   cache key for reference
  • ttl:   cache time to live
  • type:  file type (for sending)
  • val:   value

  Middleware creates a cache key based on the route and returns the entry if available.

**/

var get = require("lodash/get");
var Utils = require("./utils");
var minimatch = require("minimatch");

var Cache = module.exports = {

  store: require("lru-cache")({

    max: get(global.shipp, "config.cache.sizeLimit"),

    length: function(cached, key) {

      var val = cached.val;

      // Assume vanilla types have byte size 4, calculate all others
      var size = 4;
      if (Buffer.isBuffer(val))
        size = val.length;
      else if ("string" === typeof(val))
        size = Buffer.byteLength(val);

      // Object keys = 10 (string length of ttl, type, val), ttl = 4. Total = 14
      return size + 17 + Buffer.byteLength(key) + Buffer.byteLength(cached.type || "");

    }

  }),

  has: function(key) {
    return Cache.store.has(key);
  },

  length: function() {
    return Cache.store.length;
  },

  /**

    Gets an object from the cache.

    @param {String} key The key of the object
    @returns {*} The object

  **/

  get: function(key) {
    return Cache.store.get(key);
  },

  /**

    Sets an object in the cache.

    @param {String} key The key of the object
    @param {Object} value The object to store

  **/

  set: function(key, value) {
    if ("undefined" === typeof value)
      Cache.unset(key);
    else
      Cache.store.set(key, value);
  },

  /**

    Unsets an object in the cache.

    @param {String} key The key of the object

  **/

  unset: function(key) {
    Cache.store.del(key);
  },

  /**

    Invalidates objects in the cache via glob matching.

    @param {String} pattern Glob pattern (see https://github.com/isaacs/minimatch)
    @param {Boolean} [ignoreCase] When true, performs case insensitive match (default: true)

    @returns {Array} Array of strings invalidated

  **/

  invalidate: function(pattern, ignoreCase) {

    var keys = Cache.store.keys(),
        matches = [],
        re;

    ignoreCase = (false === ignoreCase) ? false : true;

    // minimatch.match seems to have issues with matching slashes. Use its RegExp instead.
    // We prefix gzip, etc. in order to allow the user to think of patterns in terms of "routes".
    if ("string" === typeof pattern)
      re = minimatch.makeRe("?(gzip:|identity:|deflate:)" + pattern, { nocase: ignoreCase });
    else
      re = pattern;

    if (re instanceof RegExp)
      matches = keys.filter(function(key) {
        return re.test(key);
      });
    else
      throw new Error("Pattern must be string (glob) or RegExp");

    // Delete from cache
    matches.forEach(Cache.unset);
    global.shipp.debug("Invalidated " + matches.length + " route(s) given " + pattern);

    return matches;

  },

  /**

    Clear the cache entirely, throwing away all values.

  **/

  reset: function() {
    return Cache.store.reset();
  },

  /**

    Return an array of the keys in the cache.

  **/

  keys: function() {
    return Cache.store.keys();
  },

  /**

    Middleware

    Note: we do not currently use query strings as part of caching. To do this effectively,
    certain parameters (such as those used in web tracking) must be removed.

  **/

  middleware: function() {

    var router = global.shipp.router();

    router.use("*", function(req, res, next) {

      var compression = req.acceptsEncodings(["gzip", "deflate", "identity"]) || "identity";

      // Attach cacheKey to the request
      var cacheKey = res.locals.cacheKey = compression + ":" + req.originalUrl;

      if (Cache.has(cacheKey)) {
        global.shipp.debug("Cache hit for " + cacheKey);
        var cached = Cache.get(cacheKey);
        Utils.send(res, cached.type, cached.val, compression);
      } else {
        next();
      }

    })

    global.shipp.debug("Caching middleware added");

    return router;

  }

};
<|endoftext|>


<commit_before>'use strict';

var moment = require('moment'), _ = require('lodash'), util = require('./util');

var durationOrThrow = _.partialRight(util.ensure, 'duration', moment.isDuration);
var momentOrThrow = _.partialRight(util.ensure, 'moment', moment.isMoment, function (value) {
    return value.clone();
});
var dateOrThrow = _.partialRight(util.ensure, 'date', _.isDate);
var numberOfMillisOrThrow = _.partialRight(util.ensure, 'number of milliseconds', _.isNumber);

var dateConversions = {
    numberOfMillisecondsAsDuration: function (durationInMillis) {
        return durationInMillis;
    }
};

var momentConversions = {
    numberOfMillisecondsAsDuration: function (durationInMillis) {
        return moment.duration(durationInMillis);
    }
};

var dateClockwork = {
    now: function () {
        return new Date();
    }
};

var momentClockwork = {
    now: function () {
        return moment();
    }
};

function TimeoutsAndIntervals(durationToMs) {
    return {
        setTimeout: function (callback, timeout) {
            return setTimeout(callback, durationToMs(timeout));
        },
        clearTimeout: function (id) {
            return clearTimeout(id);
        },
        setInterval: function (callback, interval) {
            return setInterval(callback, durationToMs(interval));
        },
        clearInterval: function (id) {
            return clearInterval(id);
        }
    };
}

function StubTimeoutsAndIntervals() {
    var intervals = {};
    var timeouts = {};

    return {
        setTimeout: function (callback) {
            var id = util.createGuid();
            timeouts[id] = callback;
            return id;
        },
        clearTimeout: function (id) {
            delete timeouts[id];
            return id;
        },
        setInterval: function (callback) {
            var id = util.createGuid();
            intervals[id] = callback;
            return id;
        },
        clearInterval: function (id) {
            delete intervals[id];
            return id;
        },
        triggerAll: function () {
            var ids = _.keys(timeouts).concat(_.keys(intervals));
            _.map(timeouts, function (timeout, id) {
                timeout();
                delete timeouts[id];
                return id;
            });
            _.each(intervals, function (interval) {
                interval();
            });
            return ids;
        }
    };
}

var StubClockwork = function (fns, current, tickSize, implicitTickFlag) {
    var current = current ? fns.dateCheck(current) : fns.dateFn(0);
    var tickSize = tickSize ? fns.durationCheck(tickSize) : fns.durationFn(1000);
    var implicitTickFlag = implicitTickFlag === undefined ? false : implicitTickFlag;

    return {
        now: function () {
            if (arguments.length > 0) {
                current = fns.dateCheck(arguments[0]);
            } else if (implicitTickFlag) {
                current = fns.incrementBy(current, tickSize);
            }
            return fns.dateFn(current);
        },
        tick: function (duration) {
            current = fns.incrementBy(current, duration ? fns.durationCheck(arguments[0]) : tickSize);
            return fns.dateFn(current);
        },
        tickSize: function (duration) {
            if (duration) {
                tickSize = fns.durationCheck(duration);
            }
            return tickSize;
        },
        implicitTick: function () {
            if (arguments.length > 0) {
                implicitTickFlag = arguments[0];
            }
            return implicitTickFlag;
        }
    };
};

exports.DateClock = function () {
    return _.extend(new TimeoutsAndIntervals(numberOfMillisOrThrow), dateClockwork, dateConversions);
};

exports.MomentClock = function () {
    return _.extend(new TimeoutsAndIntervals(function (duration) {
        return durationOrThrow(duration).asMilliseconds();
    }), momentClockwork, momentConversions);
};

exports.StubDateClock = function (current, tickSize, implicitTickFlag) {
    var fns = {
        dateFn: function (millis) {
            return new Date(millis);
        },
        durationFn: function (ms) {
            return ms;
        },
        incrementBy: function (date, tickSize) {
            return new Date(date.getTime() + tickSize);
        },
        dateCheck: dateOrThrow,
        durationCheck: numberOfMillisOrThrow
    };
    return _.extend(new StubTimeoutsAndIntervals(), new StubClockwork(fns, current, tickSize, implicitTickFlag), dateConversions);
};

exports.StubMomentClock = function (current, tickSize, implicitTickFlag) {
    var fns = {
        dateFn: moment,
        durationFn: function (ms) {
            return moment.duration(ms, 'milliseconds');
        },
        incrementBy: function (date, tickSize) {
            return moment(date).add(tickSize);
        },
        dateCheck: momentOrThrow,
        durationCheck: durationOrThrow
    };
    return _.extend(new StubTimeoutsAndIntervals(), new StubClockwork(fns, current, tickSize, implicitTickFlag), momentConversions);
};

exports.StubMomentClock = function (current, tickSize, implicitTickFlag) {
    var fns = {
        dateFn: moment,
        durationFn: function (ms) {
            return moment.duration(ms, 'milliseconds');
        },
        incrementBy: function (date, tickSize) {
            return moment(date).add(tickSize);
        },
        dateCheck: momentOrThrow,
        durationCheck: durationOrThrow
    };
    return _.extend(new StubTimeoutsAndIntervals(), new StubClockwork(fns, current, tickSize, implicitTickFlag), momentConversions);
};

<commit_msg>refactoring clock<commit_after>'use strict';

var moment = require('moment'), _ = require('lodash'), util = require('./util');

var durationOrThrow = _.partialRight(util.ensure, 'duration', moment.isDuration);
var momentOrThrow = _.partialRight(util.ensure, 'moment', moment.isMoment, function (value) {
    return value.clone();
});
var dateOrThrow = _.partialRight(util.ensure, 'date', _.isDate);
var numberOfMillisOrThrow = _.partialRight(util.ensure, 'number of milliseconds', _.isNumber);

var dateConversions = {
    numberOfMillisecondsAsDuration: function (durationInMillis) {
        return durationInMillis;
    }
};

var momentConversions = {
    numberOfMillisecondsAsDuration: function (durationInMillis) {
        return moment.duration(durationInMillis);
    }
};

var dateClockwork = {
    now: function () {
        return new Date();
    }
};

var momentClockwork = {
    now: function () {
        return moment();
    }
};

function TimeoutsAndIntervals(durationToMs) {
    return {
        setTimeout: function (callback, timeout) {
            return setTimeout(callback, durationToMs(timeout));
        },
        clearTimeout: function (id) {
            return clearTimeout(id);
        },
        setInterval: function (callback, interval) {
            return setInterval(callback, durationToMs(interval));
        },
        clearInterval: function (id) {
            return clearInterval(id);
        }
    };
}

function StubTimeoutsAndIntervals() {
    var intervals = {};
    var timeouts = {};

    return {
        setTimeout: function (callback) {
            var id = util.createGuid();
            timeouts[id] = callback;
            return id;
        },
        clearTimeout: function (id) {
            delete timeouts[id];
            return id;
        },
        setInterval: function (callback) {
            var id = util.createGuid();
            intervals[id] = callback;
            return id;
        },
        clearInterval: function (id) {
            delete intervals[id];
            return id;
        },
        triggerAll: function () {
            var ids = _.keys(timeouts).concat(_.keys(intervals));
            _.map(timeouts, function (timeout, id) {
                timeout();
                delete timeouts[id];
                return id;
            });
            _.each(intervals, function (interval) {
                interval();
            });
            return ids;
        }
    };
}

var StubClockwork = function (fns, current, tickSize, implicitTickFlag) {
    var current = current ? fns.dateCheck(current) : fns.dateFn(0);
    var tickSize = tickSize ? fns.durationCheck(tickSize) : fns.durationFn(1000);
    var implicitTickFlag = implicitTickFlag === undefined ? false : implicitTickFlag;

    return {
        now: function () {
            if (arguments.length > 0) {
                current = fns.dateCheck(arguments[0]);
            } else if (implicitTickFlag) {
                current = fns.incrementBy(current, tickSize);
            }
            return fns.dateFn(current);
        },
        tick: function (duration) {
            current = fns.incrementBy(current, duration ? fns.durationCheck(arguments[0]) : tickSize);
            return fns.dateFn(current);
        },
        tickSize: function (duration) {
            if (duration) {
                tickSize = fns.durationCheck(duration);
            }
            return tickSize;
        },
        implicitTick: function () {
            if (arguments.length > 0) {
                implicitTickFlag = arguments[0];
            }
            return implicitTickFlag;
        }
    };
};
exports.DateClock = function () {
    return _.extend(new TimeoutsAndIntervals(numberOfMillisOrThrow), dateClockwork, dateConversions);
};

exports.MomentClock = function () {
    return _.extend(new TimeoutsAndIntervals(function (duration) {
        return durationOrThrow(duration).asMilliseconds();
    }), momentClockwork, momentConversions);
};

exports.StubDateClock = function (current, tickSize, implicitTickFlag) {
    var fns = {
        dateFn: function (millis) {
            return new Date(millis);
        },
        durationFn: function (ms) {
            return ms;
        },
        incrementBy: function (date, tickSize) {
            return new Date(date.getTime() + tickSize);
        },
        dateCheck: dateOrThrow,
        durationCheck: numberOfMillisOrThrow
    };
    return _.extend(new StubTimeoutsAndIntervals(), new StubClockwork(fns, current, tickSize, implicitTickFlag), dateConversions);
};

exports.StubMomentClock = function (current, tickSize, implicitTickFlag) {
    var fns = {
        dateFn: moment,
        durationFn: function (ms) {
            return moment.duration(ms, 'milliseconds');
        },
        incrementBy: function (date, tickSize) {
            return moment(date).add(tickSize);
        },
        dateCheck: momentOrThrow,
        durationCheck: durationOrThrow
    };
    return _.extend(new StubTimeoutsAndIntervals(), new StubClockwork(fns, current, tickSize, implicitTickFlag), momentConversions);
};

<|endoftext|>


<commit_before>'use babel';

import fs from 'fs';

export const config = {
  cargoPath: {
    title: 'Path to the Cargo executable',
    type: 'string',
    default: 'cargo',
    order: 1
  },
  verbose: {
    title: 'Verbose Cargo output',
    description: 'Pass the --verbose flag to cargo',
    type: 'boolean',
    default: false,
    order: 2
  },
  openDocs: {
    title: 'Open documentation in browser after \'doc\' target is built',
    type: 'boolean',
    default: false,
    order: 3
  },
  showBacktrace: {
    title: 'Show backtrace information in tests',
    description: 'Set environment variable RUST_BACKTRACE=1',
    type: 'boolean',
    default: false,
    order: 4
  },
  cargoCheck: {
    title: 'Enable `cargo check',
    description: 'Enable the `cargo check` Cargo command. Only use this if you have `cargo check` installed.',
    type: 'boolean',
    default: false,
    order: 5
  },
  cargoClippy: {
    title: 'Enable `cargo clippy',
    description: 'Enable the `cargo clippy` Cargo command to run Clippy\'s lints. Only use this if you have the `cargo clippy` package installed.',
    type: 'boolean',
    default: false,
    order: 6
  },
  jsonErrors: {
    title: 'Use json errors',
    description: 'Instead of using regex to parse the human readable output (requires rustc version 1.7)\nNote: this is an unstable feature of the Rust compiler and prone to change and break frequently.',
    type: 'boolean',
    default: false,
    order: 7
  }
};

export function provideBuilder() {
  return class CargoBuildProvider {
    constructor(cwd) {
      this.cwd = cwd;
    }

    getNiceName() {
      return 'Cargo';
    }

    isEligible() {
      return fs.existsSync(`${this.cwd}/Cargo.toml`);
    }

    settings() {
      const cargoPath = atom.config.get('build-cargo.cargoPath');
      const args = [];
      atom.config.get('build-cargo.verbose') && args.push('--verbose');

      // Constants to detect links to Rust's source code and make them followable
      const unixRustSrcPrefix = '../src/';
      const windowsRustSrcPrefix = '..\\src\\';
      const rustSrcPrefixLen = unixRustSrcPrefix.length;  // Equal for both unix and windows
      const rustSrcPath = process.env.RUST_SRC_PATH;

      const docArgs = [ 'doc' ];
      atom.config.get('build-cargo.openDocs') && docArgs.push('--open');

      const env = {};
      if (atom.config.get('build-cargo.jsonErrors')) {
        env.RUSTFLAGS = '-Z unstable-options --error-format=json';
      }
      if (atom.config.get('build-cargo.showBacktrace')) {
        env.RUST_BACKTRACE = '1';
      }

      function level2severity(level) {
        switch (level) {
          case 'warning': return 'warning';
          case 'error': return 'error';
          case 'note': return 'info';
          case 'help': return 'info';
          default: return 'error';
        }
      }

      function level2type(level) {
        return level.charAt(0).toUpperCase() + level.slice(1);
      }

      // Checks if a file pointed by a message relates to the Rust source code and
      // corrects it if needed and if possible
      function normalizePath(path) {
        if (rustSrcPath) {
          const prefix = path.substring(0, rustSrcPrefixLen);
          if (prefix === unixRustSrcPrefix || prefix === windowsRustSrcPrefix) {
            return rustSrcPath + path.substring(rustSrcPrefixLen - 1); // detract 1 to preserve the original delimiter
          }
        }
        return path;
      }

      // Parses json output
      function parseJsonOutput(line, messages) {
        const json = JSON.parse(line);
        const trace = [];
        json.spans.forEach(span => {
          trace.push({
            message: span.label,
            file: span.file_name,
            line: span.line_start,
            line_end: span.line_end,
            col: span.column_start,
            col_end: span.column_end,
            type: level2type('note'),
            severity: level2severity('note')
          });
        });
        if (json.code) {
          trace.push({
            message: json.code.explanation,
            type: 'Explanation',
            severity: 'info'
          });
        }
        json.spans.forEach(span => {
          messages.push({
            message: json.message,
            file: span.file_name,
            line: span.line_start,
            line_end: span.line_end,
            col: span.column_start,
            col_end: span.column_end,
            type: level2type(json.level),
            severity: level2severity(json.level),
            trace: trace
          });
        });
      }

      function matchFunction(output) {
        const useJson = atom.config.get('build-cargo.jsonErrors');
        const messages = [];  // resulting collection of high-level messages
        let msg = null;       // current high-level message (error, warning or panic)
        let sub = null;       // current submessage (note or help)
        let match = null;

        output.split(/\n/).forEach(line => {
          // Cargo final error messages start with 'error:', skip them
          if (line === null || line === '' || line.substring(0, 6) === 'error:') {
            msg = null;
            sub = null;
          } else if (useJson && line[0] === '{') {
            // Parse a JSON block
            parseJsonOutput(line, messages);
          } else if (line.substring(0, 16) === 'stack backtrace:') {
            // Start a stacktrace submessage (it's always under a panic message)
            if (msg !== null) {
              sub = {
                message: 'Stack backtrace',
                type: 'Stack'
              };
              msg.trace.push(sub);
            }
          } else {
            // Check for compilation messages
            match = /^(.+.rs):(\d+):(\d+):(?: (\d+):(\d+))? (error|warning|help|note): (.*)/g.exec(line);
            if (match) {
              const level = match[6];
              const message = match[7];
              if (level === 'error' || level === 'warning' || msg === null) {
                msg = {
                  message: message,
                  file: normalizePath(match[1]),
                  line: match[2],
                  line_end: match[4],
                  col: match[3],
                  col_end: match[5],
                  type: level2type(level),
                  severity: level2severity(level),
                  trace: []
                };
                messages.push(msg);
                sub = null;
              } else {
                sub = {
                  message: message,
                  file: normalizePath(match[1]),
                  line: match[2],
                  line_end: match[4],
                  col: match[3],
                  col_end: match[5],
                  type: level2type(level),
                  severity: level2severity(level)
                };
                msg.trace.push(sub);
              }
            } else {
              // Check for panic
              match = /(thread '.+' panicked at '.+'), ([^\/][^\:]+):(\d+)/g.exec(line);
              if (match) {
                msg = {
                  message: match[1],
                  file: normalizePath(match[2]),
                  line: match[3],
                  type: 'Panic',
                  severity: 'error',
                  trace: []
                };
                messages.push(msg);
                sub = null;
              } else if (sub !== null) {
                // Just a description in the current block. Only add it when in submessage
                // because Linter does the job for high-level messages.
                sub.message += '\n' + line;
              }
            }
          }
        });
        return messages;
      }

      const commands = [
        {
          name: 'Cargo: build (debug)',
          exec: cargoPath,
          env: env,
          args: [ 'build' ].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:build-debug'
        },
        {
          name: 'Cargo: build (release)',
          exec: cargoPath,
          env: env,
          args: [ 'build', '--release' ].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:build-release'
        },
        {
          name: 'Cargo: bench',
          exec: cargoPath,
          env: env,
          args: [ 'bench' ].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:bench'
        },
        {
          name: 'Cargo: clean',
          exec: cargoPath,
          env: env,
          args: [ 'clean' ].concat(args),
          sh: false,
          errorMatch: [],
          atomCommandName: 'cargo:clean'
        },
        {
          name: 'Cargo: doc',
          exec: cargoPath,
          env: env,
          args: docArgs.concat(args),
          sh: false,
          errorMatch: [],
          atomCommandName: 'cargo:doc'
        },
        {
          name: 'Cargo: run (debug)',
          exec: cargoPath,
          env: env,
          args: [ 'run' ].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:run-debug'
        },
        {
          name: 'Cargo: run (release)',
          exec: cargoPath,
          env: env,
          args: [ 'run', '--release' ].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:run-release'
        },
        {
          name: 'Cargo: test',
          exec: cargoPath,
          env: env,
          args: [ 'test' ].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:run-test'
        },
        {
          name: 'Cargo: update',
          exec: cargoPath,
          env: env,
          args: [ 'update' ].concat(args),
          sh: false,
          errorMatch: [],
          atomCommandName: 'cargo:update'
        },
        {
          name: 'Cargo: build example',
          exec: cargoPath,
          env: env,
          args: [ 'build', '--example', '{FILE_ACTIVE_NAME_BASE}' ].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:build-example'
        },
        {
          name: 'Cargo: run example',
          exec: cargoPath,
          env: env,
          args: [ 'run', '--example', '{FILE_ACTIVE_NAME_BASE}' ].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:run-example'
        },
        {
          name: 'Cargo: run bin',
          exec: cargoPath,
          env: env,
          args: [ 'run', '--bin', '{FILE_ACTIVE_NAME_BASE}' ].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:run-bin'
        }
      ];

      if (atom.config.get('build-cargo.cargoClippy')) {
        commands.push({
          name: 'Cargo: Clippy',
          exec: cargoPath,
          env: env,
          args: ['clippy'].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:clippy'
        });
      }

      if (atom.config.get('build-cargo.cargoCheck')) {
        commands.push({
          name: 'Cargo: check',
          exec: cargoPath,
          env: env,
          args: ['check'].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:check'
        });
      }

      return commands;
    }
  };
}
<commit_msg>Comments improvement<commit_after>'use babel';

import fs from 'fs';

export const config = {
  cargoPath: {
    title: 'Path to the Cargo executable',
    type: 'string',
    default: 'cargo',
    order: 1
  },
  verbose: {
    title: 'Verbose Cargo output',
    description: 'Pass the --verbose flag to cargo',
    type: 'boolean',
    default: false,
    order: 2
  },
  openDocs: {
    title: 'Open documentation in browser after \'doc\' target is built',
    type: 'boolean',
    default: false,
    order: 3
  },
  showBacktrace: {
    title: 'Show backtrace information in tests',
    description: 'Set environment variable RUST_BACKTRACE=1',
    type: 'boolean',
    default: false,
    order: 4
  },
  cargoCheck: {
    title: 'Enable `cargo check',
    description: 'Enable the `cargo check` Cargo command. Only use this if you have `cargo check` installed.',
    type: 'boolean',
    default: false,
    order: 5
  },
  cargoClippy: {
    title: 'Enable `cargo clippy',
    description: 'Enable the `cargo clippy` Cargo command to run Clippy\'s lints. Only use this if you have the `cargo clippy` package installed.',
    type: 'boolean',
    default: false,
    order: 6
  },
  jsonErrors: {
    title: 'Use json errors',
    description: 'Instead of using regex to parse the human readable output (requires rustc version 1.7)\nNote: this is an unstable feature of the Rust compiler and prone to change and break frequently.',
    type: 'boolean',
    default: false,
    order: 7
  }
};

export function provideBuilder() {
  return class CargoBuildProvider {
    constructor(cwd) {
      this.cwd = cwd;
    }

    getNiceName() {
      return 'Cargo';
    }

    isEligible() {
      return fs.existsSync(`${this.cwd}/Cargo.toml`);
    }

    settings() {
      const cargoPath = atom.config.get('build-cargo.cargoPath');
      const args = [];
      atom.config.get('build-cargo.verbose') && args.push('--verbose');

      // Constants to detect links to Rust's source code and make them followable
      const unixRustSrcPrefix = '../src/';
      const windowsRustSrcPrefix = '..\\src\\';
      const rustSrcPrefixLen = unixRustSrcPrefix.length;  // Equal for both unix and windows
      const rustSrcPath = process.env.RUST_SRC_PATH;

      const docArgs = [ 'doc' ];
      atom.config.get('build-cargo.openDocs') && docArgs.push('--open');

      const env = {};
      if (atom.config.get('build-cargo.jsonErrors')) {
        env.RUSTFLAGS = '-Z unstable-options --error-format=json';
      }
      if (atom.config.get('build-cargo.showBacktrace')) {
        env.RUST_BACKTRACE = '1';
      }

      function level2severity(level) {
        switch (level) {
          case 'warning': return 'warning';
          case 'error': return 'error';
          case 'note': return 'info';
          case 'help': return 'info';
          default: return 'error';
        }
      }

      function level2type(level) {
        return level.charAt(0).toUpperCase() + level.slice(1);
      }

      // Checks if a file pointed by a message relates to the Rust source code
      // (has one of the predefined prefixes) and corrects it if needed and if possible
      function normalizePath(path) {
        if (rustSrcPath) {
          const prefix = path.substring(0, rustSrcPrefixLen);
          if (prefix === unixRustSrcPrefix || prefix === windowsRustSrcPrefix) {
            // Combine RUST_SRC_PATH with what follows after the prefix
            // Subtract 1 to preserve the original delimiter
            return rustSrcPath + path.substring(rustSrcPrefixLen - 1);
          }
        }
        return path;
      }

      // Parses json output
      function parseJsonOutput(line, messages) {
        const json = JSON.parse(line);
        const trace = [];
        json.spans.forEach(span => {
          trace.push({
            message: span.label,
            file: span.file_name,
            line: span.line_start,
            line_end: span.line_end,
            col: span.column_start,
            col_end: span.column_end,
            type: level2type('note'),
            severity: level2severity('note')
          });
        });
        if (json.code) {
          trace.push({
            message: json.code.explanation,
            type: 'Explanation',
            severity: 'info'
          });
        }
        json.spans.forEach(span => {
          messages.push({
            message: json.message,
            file: span.file_name,
            line: span.line_start,
            line_end: span.line_end,
            col: span.column_start,
            col_end: span.column_end,
            type: level2type(json.level),
            severity: level2severity(json.level),
            trace: trace
          });
        });
      }

      function matchFunction(output) {
        const useJson = atom.config.get('build-cargo.jsonErrors');
        const messages = [];  // resulting collection of high-level messages
        let msg = null;       // current high-level message (error, warning or panic)
        let sub = null;       // current submessage (note or help)
        let match = null;

        output.split(/\n/).forEach(line => {
          // Cargo final error messages start with 'error:', skip them
          if (line === null || line === '' || line.substring(0, 6) === 'error:') {
            msg = null;
            sub = null;
          } else if (useJson && line[0] === '{') {
            // Parse a JSON block
            parseJsonOutput(line, messages);
          } else if (line.substring(0, 16) === 'stack backtrace:') {
            // Start a stacktrace submessage (it's always under a panic message)
            if (msg !== null) {
              sub = {
                message: 'Stack backtrace',
                type: 'Stack'
              };
              msg.trace.push(sub);
            }
          } else {
            // Check for compilation messages
            match = /^(.+.rs):(\d+):(\d+):(?: (\d+):(\d+))? (error|warning|help|note): (.*)/g.exec(line);
            if (match) {
              const level = match[6];
              const message = match[7];
              if (level === 'error' || level === 'warning' || msg === null) {
                msg = {
                  message: message,
                  file: normalizePath(match[1]),
                  line: match[2],
                  line_end: match[4],
                  col: match[3],
                  col_end: match[5],
                  type: level2type(level),
                  severity: level2severity(level),
                  trace: []
                };
                messages.push(msg);
                sub = null;
              } else {
                sub = {
                  message: message,
                  file: normalizePath(match[1]),
                  line: match[2],
                  line_end: match[4],
                  col: match[3],
                  col_end: match[5],
                  type: level2type(level),
                  severity: level2severity(level)
                };
                msg.trace.push(sub);
              }
            } else {
              // Check for panic
              match = /(thread '.+' panicked at '.+'), ([^\/][^\:]+):(\d+)/g.exec(line);
              if (match) {
                msg = {
                  message: match[1],
                  file: normalizePath(match[2]),
                  line: match[3],
                  type: 'Panic',
                  severity: 'error',
                  trace: []
                };
                messages.push(msg);
                sub = null;
              } else if (sub !== null) {
                // Just a description in the current block. Only add it when in submessage
                // because Linter does the job for high-level messages.
                sub.message += '\n' + line;
              }
            }
          }
        });
        return messages;
      }

      const commands = [
        {
          name: 'Cargo: build (debug)',
          exec: cargoPath,
          env: env,
          args: [ 'build' ].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:build-debug'
        },
        {
          name: 'Cargo: build (release)',
          exec: cargoPath,
          env: env,
          args: [ 'build', '--release' ].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:build-release'
        },
        {
          name: 'Cargo: bench',
          exec: cargoPath,
          env: env,
          args: [ 'bench' ].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:bench'
        },
        {
          name: 'Cargo: clean',
          exec: cargoPath,
          env: env,
          args: [ 'clean' ].concat(args),
          sh: false,
          errorMatch: [],
          atomCommandName: 'cargo:clean'
        },
        {
          name: 'Cargo: doc',
          exec: cargoPath,
          env: env,
          args: docArgs.concat(args),
          sh: false,
          errorMatch: [],
          atomCommandName: 'cargo:doc'
        },
        {
          name: 'Cargo: run (debug)',
          exec: cargoPath,
          env: env,
          args: [ 'run' ].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:run-debug'
        },
        {
          name: 'Cargo: run (release)',
          exec: cargoPath,
          env: env,
          args: [ 'run', '--release' ].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:run-release'
        },
        {
          name: 'Cargo: test',
          exec: cargoPath,
          env: env,
          args: [ 'test' ].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:run-test'
        },
        {
          name: 'Cargo: update',
          exec: cargoPath,
          env: env,
          args: [ 'update' ].concat(args),
          sh: false,
          errorMatch: [],
          atomCommandName: 'cargo:update'
        },
        {
          name: 'Cargo: build example',
          exec: cargoPath,
          env: env,
          args: [ 'build', '--example', '{FILE_ACTIVE_NAME_BASE}' ].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:build-example'
        },
        {
          name: 'Cargo: run example',
          exec: cargoPath,
          env: env,
          args: [ 'run', '--example', '{FILE_ACTIVE_NAME_BASE}' ].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:run-example'
        },
        {
          name: 'Cargo: run bin',
          exec: cargoPath,
          env: env,
          args: [ 'run', '--bin', '{FILE_ACTIVE_NAME_BASE}' ].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:run-bin'
        }
      ];

      if (atom.config.get('build-cargo.cargoClippy')) {
        commands.push({
          name: 'Cargo: Clippy',
          exec: cargoPath,
          env: env,
          args: ['clippy'].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:clippy'
        });
      }

      if (atom.config.get('build-cargo.cargoCheck')) {
        commands.push({
          name: 'Cargo: check',
          exec: cargoPath,
          env: env,
          args: ['check'].concat(args),
          sh: false,
          functionMatch: matchFunction,
          atomCommandName: 'cargo:check'
        });
      }

      return commands;
    }
  };
}
<|endoftext|>


<commit_before>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

/*
 * Copyright (c) 2014, Joyent, Inc.
 */

/*
 * CNAPI: workflow shared functions
 */

// These must match the names available in the workflow VM:
var async = require('async');
var restify = require('restify');
var sdcClients = require('sdc-clients');

// --- Globals

// Make jslint happy:
var cnapiUrl;

// --- Exports

/**
 * Start a provisioner task with CNAPI on each of the servers to update
 * the firewall data.
 *
 * Inputs:
 * - job.params.fwapiServers {Array} : server UUIDs to send update tasks to
 *
 * Outputs:
 * - job.params.taskIDs {Array}: task objects to poll in a later task, where
 *   each task object looks like:
 *     { server_uuid: uuid, task_id: id }
 */
function fwUpdate(job, callback) {
    if (!job.params.fwapiServers || job.params.fwapiServers.length === 0) {
        return callback(null, 'No remote servers to update');
    }

    var serversSent = [];
    var cnapi = new sdcClients.CNAPI({ url: cnapiUrl });
    var matchingVMs = job.params.fwapiMatchingVMs || [];
    job.params.taskIDs = [];

    return async.forEachSeries(job.params.fwapiServers, function (uuid, cb) {
        var endpoint = '/servers/' + uuid + '/fw/update';
        var firewall = {
            jobid: job.uuid
        };

        if (job.params.hasOwnProperty('rule')) {
            firewall.rules = [ job.params.rule ];
        }

        var remoteVMs = matchingVMs.filter(function (rvm) {
            return (rvm.server_uuid != uuid);
        });

        if (remoteVMs.length) {
            firewall.remoteVMs = remoteVMs;
        }

        job.log.debug(firewall, 'Updating rules on server "%s"', uuid);
        return cnapi.post(endpoint, firewall, function (err, task) {
            if (err) {
                if (err.restCode == 'InvalidVersion') {
                    // This is a 6.5 CN - no firewall data was sent to it
                    return cb(null);
                }

                return cb(err);
            }

            serversSent.push(uuid);
            job.log.debug(task, 'Server "%s": task', uuid);

            job.params.taskIDs.push({ server_uuid: uuid, task_id: task.id});
            return cb(null);
        });

    }, function (err) {
        if (err) {
            return callback(err);
        }

        if (serversSent.length === 0) {
            return callback(null,
                    'Only 6.5 servers available: did not send firewall data');
        }

        return callback(null, 'Started update on servers: '
            + serversSent.join(', '));
    });
}

/**
 * Get the server sysinfo objects for the given UUIDs
 *
 * Inputs:
 *
 * - job.params {Object} :
 *   - serverUUIDs {Array of UUIDs} : UUIDS of servers to get sysinfos for
 *
 * - job.servers {Array} : (optional) If present, as many sysinfo objects as
 *   possible will be pulled from here in an attempt to avoid calling out to
 *   CNAPI
 *
 * Outputs:
 *
 * Once function is complete, the following will be stored in job.params:
 * - cnapiSysinfos {Object} : CNAPI server objects containing a sysinfo
 *   subobject
 */
function sysinfoList(job, callback) {
        if (!job.params.serverUUIDs) {
            return callback(null, 'No server UUIDs to fetch');
        }

        var cnapi = new sdcClients.CNAPI({ url: cnapiUrl });
        var params = {
            extras: 'sysinfo',
            uuids: job.params.serverUUIDs.join(',')
        };
        job.params.cnapiSysinfos = [];

        // If we have job.servers from an earlier task, use as many of those
        // as we can to try and avoid hitting CNAPI again
        if (job.servers) {
            var uuids = {};
            job.params.serverUUIDs.forEach(function (uuid) {
                uuids[uuid] = 1;
            });

            job.servers.forEach(function (s) {
                if (uuids.hasOwnProperty(s.uuid)) {
                    delete uuids[s.uuid];
                    job.params.cnapiSysinfos.push(s);
                }
            });
            params.uuids = Object.keys(uuids);
        }

        if (params.uuids.length === 0) {
            return callback(null,
                'Server info cached from get_servers: not fetching');
        }

        return cnapi.listServers(params, function (err, servers) {
            if (err) {
                return callback(err);
            }

            if (!Array.isArray(servers) || servers.length === 0) {
                return callback(new Error('No servers found'));
            }

            job.params.cnapiSysinfos = job.params.cnapiSysinfos.concat(servers);
            job.log.debug(servers.map(function (s) { return s.UUID; }),
                'getServers: servers found');

            return callback(null, 'Got servers');
        });
}

/**
 * Start a provisioner task with CNAPI on a servers to update nics.
 *
 * Inputs:
 * - job.params.server_uuid {UUID} : server UUIDs to send update task to
 * - job.params.nics {Array}: nic objects to update
 *
 * Outputs:
 * - job.params.taskIDs {Array}: task objects to poll in a later task, where
 *   each task object looks like:
 *     { server_uuid: uuid, task_id: id }
 */
function nicUpdate(job, callback) {
        if (!job.params.nics || job.params.nics.length === 0) {
            return callback(null, 'No nics to update');
        }

        var cnapi = new sdcClients.CNAPI({ url: cnapiUrl });
        var endpoint = '/servers/' + job.params.server_uuid + '/nics/update';
        job.params.taskIDs = [];

        job.log.debug(job.params.nics, 'Updating rules on server "%s"',
            job.params.server_uuid);

        return cnapi.post(endpoint, { nics: job.params.nics },
            function (err, task) {
            if (err) {
                return callback(err);
            }

            job.log.debug(task, 'Server "%s": nic update task "%s"',
                job.params.server_uuid, task.id);
            job.params.taskIDs.push({ server_uuid: job.params.server_uuid,
                task_id: task.id});
            return callback(null, 'Started nic update task on server');
        });
}

/**
 * Poll CNAPI for each of the tasks started in a previous workflow task
 *
 * Inputs:
 * - job.params.taskIDs {Array} : task ID objects as per fwUpdate()
 *
 * Outputs:
 * - job.params.taskSuccesses {Array} : task ID objects of successes
 * - job.params.taskFailures {Array} : task ID objects of failures
 */
function pollTasks(job, callback) {
    if (!job.params.taskIDs || job.params.taskIDs.length === 0) {
        return callback(null, 'No tasks to poll');
    }

    var cnapi = new sdcClients.CNAPI({ url: cnapiUrl });

    job.params.taskSuccesses = [];
    job.params.taskFailures = [];

    return async.forEach(job.params.taskIDs, function (detail, cb) {
        var timeout = null;

        function pollOne() {
            cnapi.getTask(detail.task_id, function onCnapi(err, task) {
                if (timeout) {
                    clearTimeout(timeout);
                }

                if (err) {
                    return cb(err);
                }

                job.log.debug(task, 'retrieved task for server "%s"',
                    detail.server_uuid);
                if (task.status == 'failure') {
                    job.params.taskFailures.push(detail);
                    return cb(new Error('Job "' + detail.task_id
                        + '" failed for server "' + detail.server_uuid + '"'));
                }

                if (task.status == 'complete') {
                    job.params.taskSuccesses.push(detail);
                    return cb(null);
                }

                timeout = setTimeout(pollOne, 1000);
            });
        }

        pollOne();
    }, function (err) {
        if (err) {
            return callback(err);
        }

        return callback(null, 'All server tasks returned successfully');
    });
}

/**
 * Trigger a sysinfo refresh in CNAPI
 *
 * Inputs:
 * - job.params.taskIDs {Array} : task ID objects as per fwUpdate()
 *
 * Outputs:
 * - job.params.taskSuccesses {Array} : task ID objects of successes
 * - job.params.taskFailures {Array} : task ID objects of failures
 */
function refreshServerSysinfo(job, callback) {
    var cnapi = restify.createJsonClient({ url: cnapiUrl});
    var serverUrl = '/servers/' + job.params.server_uuid + '/sysinfo-refresh';

    cnapi.post(serverUrl, {}, function (error, req, res) {
        if (error) {
            job.log.info('Error refreshing server sysinfo');
            job.log.info(error.stack.toString());
            callback(error);
            return;
        }
        callback();
    });
}

module.exports = {
    fwUpdate: fwUpdate,
    sysinfoList: sysinfoList,
    nicUpdate: nicUpdate,
    pollTasks: pollTasks,
    refreshServerSysinfo: refreshServerSysinfo
};
<commit_msg>TOOLS-1373: NAPI UpdateNics job step says "Job $uuid failed for server $uuid" when it means a *CNAPI task* failed<commit_after>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

/*
 * Copyright (c) 2014, Joyent, Inc.
 */

/*
 * CNAPI: workflow shared functions
 */

// These must match the names available in the workflow VM:
var async = require('async');
var restify = require('restify');
var sdcClients = require('sdc-clients');

// --- Globals

// Make jslint happy:
var cnapiUrl;

// --- Exports

/**
 * Start a provisioner task with CNAPI on each of the servers to update
 * the firewall data.
 *
 * Inputs:
 * - job.params.fwapiServers {Array} : server UUIDs to send update tasks to
 *
 * Outputs:
 * - job.params.taskIDs {Array}: task objects to poll in a later task, where
 *   each task object looks like:
 *     { server_uuid: uuid, task_id: id }
 */
function fwUpdate(job, callback) {
    if (!job.params.fwapiServers || job.params.fwapiServers.length === 0) {
        return callback(null, 'No remote servers to update');
    }

    var serversSent = [];
    var cnapi = new sdcClients.CNAPI({ url: cnapiUrl });
    var matchingVMs = job.params.fwapiMatchingVMs || [];
    job.params.taskIDs = [];

    return async.forEachSeries(job.params.fwapiServers, function (uuid, cb) {
        var endpoint = '/servers/' + uuid + '/fw/update';
        var firewall = {
            jobid: job.uuid
        };

        if (job.params.hasOwnProperty('rule')) {
            firewall.rules = [ job.params.rule ];
        }

        var remoteVMs = matchingVMs.filter(function (rvm) {
            return (rvm.server_uuid != uuid);
        });

        if (remoteVMs.length) {
            firewall.remoteVMs = remoteVMs;
        }

        job.log.debug(firewall, 'Updating rules on server "%s"', uuid);
        return cnapi.post(endpoint, firewall, function (err, task) {
            if (err) {
                if (err.restCode == 'InvalidVersion') {
                    // This is a 6.5 CN - no firewall data was sent to it
                    return cb(null);
                }

                return cb(err);
            }

            serversSent.push(uuid);
            job.log.debug(task, 'Server "%s": task', uuid);

            job.params.taskIDs.push({ server_uuid: uuid, task_id: task.id});
            return cb(null);
        });

    }, function (err) {
        if (err) {
            return callback(err);
        }

        if (serversSent.length === 0) {
            return callback(null,
                    'Only 6.5 servers available: did not send firewall data');
        }

        return callback(null, 'Started update on servers: '
            + serversSent.join(', '));
    });
}

/**
 * Get the server sysinfo objects for the given UUIDs
 *
 * Inputs:
 *
 * - job.params {Object} :
 *   - serverUUIDs {Array of UUIDs} : UUIDS of servers to get sysinfos for
 *
 * - job.servers {Array} : (optional) If present, as many sysinfo objects as
 *   possible will be pulled from here in an attempt to avoid calling out to
 *   CNAPI
 *
 * Outputs:
 *
 * Once function is complete, the following will be stored in job.params:
 * - cnapiSysinfos {Object} : CNAPI server objects containing a sysinfo
 *   subobject
 */
function sysinfoList(job, callback) {
        if (!job.params.serverUUIDs) {
            return callback(null, 'No server UUIDs to fetch');
        }

        var cnapi = new sdcClients.CNAPI({ url: cnapiUrl });
        var params = {
            extras: 'sysinfo',
            uuids: job.params.serverUUIDs.join(',')
        };
        job.params.cnapiSysinfos = [];

        // If we have job.servers from an earlier task, use as many of those
        // as we can to try and avoid hitting CNAPI again
        if (job.servers) {
            var uuids = {};
            job.params.serverUUIDs.forEach(function (uuid) {
                uuids[uuid] = 1;
            });

            job.servers.forEach(function (s) {
                if (uuids.hasOwnProperty(s.uuid)) {
                    delete uuids[s.uuid];
                    job.params.cnapiSysinfos.push(s);
                }
            });
            params.uuids = Object.keys(uuids);
        }

        if (params.uuids.length === 0) {
            return callback(null,
                'Server info cached from get_servers: not fetching');
        }

        return cnapi.listServers(params, function (err, servers) {
            if (err) {
                return callback(err);
            }

            if (!Array.isArray(servers) || servers.length === 0) {
                return callback(new Error('No servers found'));
            }

            job.params.cnapiSysinfos = job.params.cnapiSysinfos.concat(servers);
            job.log.debug(servers.map(function (s) { return s.UUID; }),
                'getServers: servers found');

            return callback(null, 'Got servers');
        });
}

/**
 * Start a provisioner task with CNAPI on a servers to update nics.
 *
 * Inputs:
 * - job.params.server_uuid {UUID} : server UUIDs to send update task to
 * - job.params.nics {Array}: nic objects to update
 *
 * Outputs:
 * - job.params.taskIDs {Array}: task objects to poll in a later task, where
 *   each task object looks like:
 *     { server_uuid: uuid, task_id: id }
 */
function nicUpdate(job, callback) {
        if (!job.params.nics || job.params.nics.length === 0) {
            return callback(null, 'No nics to update');
        }

        var cnapi = new sdcClients.CNAPI({ url: cnapiUrl });
        var endpoint = '/servers/' + job.params.server_uuid + '/nics/update';
        job.params.taskIDs = [];

        job.log.debug(job.params.nics, 'Updating rules on server "%s"',
            job.params.server_uuid);

        return cnapi.post(endpoint, { nics: job.params.nics },
            function (err, task) {
            if (err) {
                return callback(err);
            }

            job.log.debug(task, 'Server "%s": nic update task "%s"',
                job.params.server_uuid, task.id);
            job.params.taskIDs.push({ server_uuid: job.params.server_uuid,
                task_id: task.id});
            return callback(null, 'Started nic update task on server');
        });
}

/**
 * Poll CNAPI for each of the tasks started in a previous workflow task
 *
 * Inputs:
 * - job.params.taskIDs {Array} : task ID objects as per fwUpdate()
 *
 * Outputs:
 * - job.params.taskSuccesses {Array} : task ID objects of successes
 * - job.params.taskFailures {Array} : task ID objects of failures
 */
function pollTasks(job, callback) {
    if (!job.params.taskIDs || job.params.taskIDs.length === 0) {
        return callback(null, 'No tasks to poll');
    }

    var cnapi = new sdcClients.CNAPI({ url: cnapiUrl });

    job.params.taskSuccesses = [];
    job.params.taskFailures = [];

    return async.forEach(job.params.taskIDs, function (detail, cb) {
        var timeout = null;

        function pollOne() {
            cnapi.getTask(detail.task_id, function onCnapi(err, task) {
                if (timeout) {
                    clearTimeout(timeout);
                }

                if (err) {
                    return cb(err);
                }

                job.log.debug(task, 'retrieved task for server "%s"',
                    detail.server_uuid);
                if (task.status == 'failure') {
                    job.params.taskFailures.push(detail);
                    return cb(new Error('CNAPI task "' + detail.task_id
                        + '" failed for server "' + detail.server_uuid + '"'));
                }

                if (task.status == 'complete') {
                    job.params.taskSuccesses.push(detail);
                    return cb(null);
                }

                timeout = setTimeout(pollOne, 1000);
            });
        }

        pollOne();
    }, function (err) {
        if (err) {
            return callback(err);
        }

        return callback(null, 'All server tasks returned successfully');
    });
}

/**
 * Trigger a sysinfo refresh in CNAPI
 *
 * Inputs:
 * - job.params.taskIDs {Array} : task ID objects as per fwUpdate()
 *
 * Outputs:
 * - job.params.taskSuccesses {Array} : task ID objects of successes
 * - job.params.taskFailures {Array} : task ID objects of failures
 */
function refreshServerSysinfo(job, callback) {
    var cnapi = restify.createJsonClient({ url: cnapiUrl});
    var serverUrl = '/servers/' + job.params.server_uuid + '/sysinfo-refresh';

    cnapi.post(serverUrl, {}, function (error, req, res) {
        if (error) {
            job.log.info('Error refreshing server sysinfo');
            job.log.info(error.stack.toString());
            callback(error);
            return;
        }
        callback();
    });
}

module.exports = {
    fwUpdate: fwUpdate,
    sysinfoList: sysinfoList,
    nicUpdate: nicUpdate,
    pollTasks: pollTasks,
    refreshServerSysinfo: refreshServerSysinfo
};
<|endoftext|>