<commit_before>'use babel'

import {Emitter, CompositeDisposable} from 'atom'
export default class EditorLinter {
  constructor(editor) {
    if (typeof editor !== 'object' || typeof editor.markBufferRange !== 'function') {
      throw new Error('Given editor is not really an editor')
    }

    this.editor = editor
    this.emitter = new Emitter()
    this.messages = new Set()
    this.markers = new WeakMap()
    this.gutter = null
    this.subscriptions = new CompositeDisposable

    this.subscriptions.add(atom.config.observe('linter.underlineIssues', underlineIssues =>
      this.underlineIssues = underlineIssues
    ))
    this.subscriptions.add(this.editor.onDidDestroy(() =>
      this.dispose()
    ))
    this.subscriptions.add(this.editor.onDidSave(() =>
      this.emitter.emit('should-lint', false)
    ))
    this.subscriptions.add(this.editor.onDidChangeCursorPosition(({oldBufferPosition, newBufferPosition}) => {
      if (newBufferPosition.row !== oldBufferPosition.row) {
        this.emitter.emit('should-update-line-messages')
      }
      this.emitter.emit('should-update-bubble')
    }))
    this.subscriptions.add(atom.config.observe('linter.gutterEnabled', gutterEnabled => {
      this.gutterEnabled = gutterEnabled
      this.handleGutter()
    }))
    // Using onDidChange instead of observe here 'cause the same function is invoked above
    this.subscriptions.add(atom.config.onDidChange('linter.gutterPosition', () =>
      this.handleGutter()
    ))
    this.subscriptions.add(this.onDidMessageAdd(message => {
      if (!this.underlineIssues && !this.gutterEnabled) {
        return // No-Op
      }
      const marker = this.editor.markBufferRange(message.range, {invalidate: 'inside'})
      this.markers.set(message, marker)
      if (this.underlineIssues) {
        this.editor.decorateMarker(marker, {
          type: 'highlight',
          class: `linter-highlight ${message.class}`
        })
      }
      if (this.gutterEnabled) {
        const item = document.createElement('span')
        item.className = `linter-gutter linter-highlight ${message.class}`
        this.gutter.decorateMarker(marker, {
          class: 'linter-row',
          item
        })
      }
    }))
    this.subscriptions.add(this.onDidMessageDelete(message => {
      if (this.markers.has(message)) {
        this.markers.get(message).destroy()
        this.markers.delete(message)
      }
    }))

    // Atom invokes the onDidStopChanging callback immediately on Editor creation. So we wait a moment
    setImmediate(() => {
      this.subscriptions.add(this.editor.onDidStopChanging(() =>
        this.emitter.emit('should-lint', true)
      ))
    })
  }

  handleGutter() {
    if (this.gutter !== null) {
      this.removeGutter()
    }
    if (this.gutterEnabled) {
      this.addGutter()
    }
  }

  addGutter() {
    const position = atom.config.get('linter.gutterPosition')
    this.gutter = this.editor.addGutter({
      name: 'linter',
      priority: position === 'Left' ? -100 : 100
    })
  }

  removeGutter() {
    if (this.gutter !== null) {
      try {
        this.gutter.destroy()
        // Atom throws when we try to remove a gutter container from a closed text editor
      } catch (err) {}
      this.gutter = null
    }
  }

  getMessages() {
    return this.messages
  }

  addMessage(message) {
    if (!this.messages.has(message)) {
      this.messages.add(message)
      this.emitter.emit('did-message-add', message)
      this.emitter.emit('did-message-change', {message, type: 'add'})
    }
  }

  deleteMessage(message) {
    if (this.messages.has(message)) {
      this.messages.delete(message)
      this.emitter.emit('did-message-delete', message)
      this.emitter.emit('did-message-change', {message, type: 'delete'})
    }
  }

  lint(onChange = false) {
    this.emitter.emit('should-lint', onChange)
  }

  onDidMessageAdd(callback) {
    return this.emitter.on('did-message-add', callback)
  }

  onDidMessageDelete(callback) {
    return this.emitter.on('did-message-delete', callback)
  }

  onDidMessageChange(callback) {
    return this.emitter.on('did-message-change', callback)
  }

  onShouldUpdateBubble(callback) {
    return this.emitter.on('should-update-bubble', callback)
  }

  onShouldUpdateLineMessages(callback) {
    return this.emitter.on('should-update-line-messages', callback)
  }

  onShouldLint(callback) {
    return this.emitter.on('should-lint', callback)
  }

  onDidDestroy(callback) {
    return this.emitter.on('did-destroy', callback)
  }

  dispose() {
    this.emitter.emit('did-destroy')
    if (this.markers.size) {
      this.markers.forEach(marker => marker.destroy())
      this.markers.clear()
    }
    this.removeGutter()
    this.subscriptions.dispose()
    this.messages.clear()
    this.emitter.dispose()
  }
}
<commit_msg>:art: Make EditorLinter::messages a weakmap<commit_after>'use babel'

import {Emitter, CompositeDisposable} from 'atom'
export default class EditorLinter {
  constructor(editor) {
    if (typeof editor !== 'object' || typeof editor.markBufferRange !== 'function') {
      throw new Error('Given editor is not really an editor')
    }

    this.editor = editor
    this.emitter = new Emitter()
    this.messages = new WeakSet()
    this.markers = new WeakMap()
    this.gutter = null
    this.subscriptions = new CompositeDisposable

    this.subscriptions.add(atom.config.observe('linter.underlineIssues', underlineIssues =>
      this.underlineIssues = underlineIssues
    ))
    this.subscriptions.add(this.editor.onDidDestroy(() =>
      this.dispose()
    ))
    this.subscriptions.add(this.editor.onDidSave(() =>
      this.emitter.emit('should-lint', false)
    ))
    this.subscriptions.add(this.editor.onDidChangeCursorPosition(({oldBufferPosition, newBufferPosition}) => {
      if (newBufferPosition.row !== oldBufferPosition.row) {
        this.emitter.emit('should-update-line-messages')
      }
      this.emitter.emit('should-update-bubble')
    }))
    this.subscriptions.add(atom.config.observe('linter.gutterEnabled', gutterEnabled => {
      this.gutterEnabled = gutterEnabled
      this.handleGutter()
    }))
    // Using onDidChange instead of observe here 'cause the same function is invoked above
    this.subscriptions.add(atom.config.onDidChange('linter.gutterPosition', () =>
      this.handleGutter()
    ))
    this.subscriptions.add(this.onDidMessageAdd(message => {
      if (!this.underlineIssues && !this.gutterEnabled) {
        return // No-Op
      }
      const marker = this.editor.markBufferRange(message.range, {invalidate: 'inside'})
      this.markers.set(message, marker)
      if (this.underlineIssues) {
        this.editor.decorateMarker(marker, {
          type: 'highlight',
          class: `linter-highlight ${message.class}`
        })
      }
      if (this.gutterEnabled) {
        const item = document.createElement('span')
        item.className = `linter-gutter linter-highlight ${message.class}`
        this.gutter.decorateMarker(marker, {
          class: 'linter-row',
          item
        })
      }
    }))
    this.subscriptions.add(this.onDidMessageDelete(message => {
      if (this.markers.has(message)) {
        this.markers.get(message).destroy()
        this.markers.delete(message)
      }
    }))

    // Atom invokes the onDidStopChanging callback immediately on Editor creation. So we wait a moment
    setImmediate(() => {
      this.subscriptions.add(this.editor.onDidStopChanging(() =>
        this.emitter.emit('should-lint', true)
      ))
    })
  }

  handleGutter() {
    if (this.gutter !== null) {
      this.removeGutter()
    }
    if (this.gutterEnabled) {
      this.addGutter()
    }
  }

  addGutter() {
    const position = atom.config.get('linter.gutterPosition')
    this.gutter = this.editor.addGutter({
      name: 'linter',
      priority: position === 'Left' ? -100 : 100
    })
  }

  removeGutter() {
    if (this.gutter !== null) {
      try {
        this.gutter.destroy()
        // Atom throws when we try to remove a gutter container from a closed text editor
      } catch (err) {}
      this.gutter = null
    }
  }

  addMessage(message) {
    if (!this.messages.has(message)) {
      this.messages.add(message)
      this.emitter.emit('did-message-add', message)
      this.emitter.emit('did-message-change', {message, type: 'add'})
    }
  }

  deleteMessage(message) {
    if (this.messages.has(message)) {
      this.messages.delete(message)
      this.emitter.emit('did-message-delete', message)
      this.emitter.emit('did-message-change', {message, type: 'delete'})
    }
  }

  lint(onChange = false) {
    this.emitter.emit('should-lint', onChange)
  }

  onDidMessageAdd(callback) {
    return this.emitter.on('did-message-add', callback)
  }

  onDidMessageDelete(callback) {
    return this.emitter.on('did-message-delete', callback)
  }

  onDidMessageChange(callback) {
    return this.emitter.on('did-message-change', callback)
  }

  onShouldUpdateBubble(callback) {
    return this.emitter.on('should-update-bubble', callback)
  }

  onShouldUpdateLineMessages(callback) {
    return this.emitter.on('should-update-line-messages', callback)
  }

  onShouldLint(callback) {
    return this.emitter.on('should-lint', callback)
  }

  onDidDestroy(callback) {
    return this.emitter.on('did-destroy', callback)
  }

  dispose() {
    this.emitter.emit('did-destroy')
    if (this.markers.size) {
      this.markers.forEach(marker => marker.destroy())
      this.markers.clear()
    }
    this.removeGutter()
    this.subscriptions.dispose()
    this.messages.clear()
    this.emitter.dispose()
  }
}
<|endoftext|>


<commit_before>'use babel'

import {Emitter, CompositeDisposable} from 'atom'
export default class EditorLinter {
  constructor(editor, isActive) {
    if (typeof editor !== 'object' || typeof editor.markBufferRange !== 'function') {
      throw new Error('Given editor is not really an editor')
    }

    this.editor = editor
    this.emitter = new Emitter()
    this.messages = new Set()
    this.markers = new WeakMap()
    this.gutter = null
    this.subscriptions = new CompositeDisposable

    this.subscriptions.add(atom.config.observe('linter.underlineIssues', underlineIssues =>
      this.underlineIssues = underlineIssues
    ))
    this.subscriptions.add(this.editor.onDidDestroy(() =>
      this.dispose()
    ))
    this.subscriptions.add(this.editor.onDidSave(() =>
      this.emitter.emit('should-lint', false)
    ))
    this.subscriptions.add(this.editor.onDidChangeCursorPosition(({oldBufferPosition, newBufferPosition}) => {
      if (newBufferPosition.row !== oldBufferPosition.row) {
        // TODO: Handle line messages in editor linter itself
        this.emitter.emit('should-update-line-messages')
      }
      this.emitter.emit('should-update-bubble')
    }))
    this.subscriptions.add(atom.config.observe('linter.gutterEnabled', gutterEnabled => {
      this.gutterEnabled = gutterEnabled
      this.handleGutter()
    }))
    // Using onDidChange instead of observe here 'cause the same function is invoked above
    this.subscriptions.add(atom.config.onDidChange('linter.gutterPosition', () =>
      this.handleGutter()
    ))
    this.subscriptions.add(this.onDidMessageAdd(message => {
      if (!this.underlineIssues && !this.gutterEnabled) {
        return // No-Op
      }
      const marker = this.editor.markBufferRange(message.range, {invalidate: 'inside'})
      this.markers.set(message, marker)
      if (this.underlineIssues) {
        this.editor.decorateMarker(marker, {
          type: 'highlight',
          class: `linter-highlight ${message.class}`
        })
      }
      if (this.gutterEnabled) {
        const item = document.createElement('span')
        item.className = `linter-gutter linter-highlight ${message.class}`
        this.gutter.decorateMarker(marker, {
          class: 'linter-row',
          item
        })
      }
    }))
    this.subscriptions.add(this.onDidMessageDelete(message => {
      if (this.markers.has(message)) {
        this.markers.get(message).destroy()
        this.markers.delete(message)
      }
    }))

    // Atom invokes the onDidStopChanging callback immediately on Editor creation. So we wait a moment
    setImmediate(() => {
      this.subscriptions.add(this.editor.onDidStopChanging(() =>
        this.emitter.emit('should-lint', true)
      ))
    })

    this.active = isActive
  }

  set active(value) {
    value = Boolean(value)
    if (value !== this._active) {
      this._active = value
      if (this.messages.size) {
        this.messages.forEach(message => message.currentFile = value)
      }
    }
  }
  get active() {
    return this._active
  }

  handleGutter() {
    if (this.gutter !== null) {
      this.removeGutter()
    }
    if (this.gutterEnabled) {
      this.addGutter()
    }
  }

  addGutter() {
    const position = atom.config.get('linter.gutterPosition')
    this.gutter = this.editor.addGutter({
      name: 'linter',
      priority: position === 'Left' ? -100 : 100
    })
  }

  removeGutter() {
    if (this.gutter !== null) {
      try {
        // Atom throws when we try to remove a gutter container from a closed text editor
        this.gutter.destroy()
      } catch (err) {}
      this.gutter = null
    }
  }

  getMessages() {
    return this.messages
  }

  addMessage(message) {
    if (!this.messages.has(message)) {
      if (this.active) {
        message.currentFile = true
      }
      this.messages.add(message)
      this.emitter.emit('did-message-add', message)
      this.emitter.emit('did-message-change', {message, type: 'add'})
    }
  }

  deleteMessage(message) {
    if (this.messages.has(message)) {
      this.messages.delete(message)
      this.emitter.emit('did-message-delete', message)
      this.emitter.emit('did-message-change', {message, type: 'delete'})
    }
  }

  lint(onChange = false) {
    this.emitter.emit('should-lint', onChange)
  }

  onDidMessageAdd(callback) {
    return this.emitter.on('did-message-add', callback)
  }

  onDidMessageDelete(callback) {
    return this.emitter.on('did-message-delete', callback)
  }

  onDidMessageChange(callback) {
    return this.emitter.on('did-message-change', callback)
  }

  onShouldUpdateBubble(callback) {
    return this.emitter.on('should-update-bubble', callback)
  }

  onShouldUpdateLineMessages(callback) {
    return this.emitter.on('should-update-line-messages', callback)
  }

  onShouldLint(callback) {
    return this.emitter.on('should-lint', callback)
  }

  onDidDestroy(callback) {
    return this.emitter.on('did-destroy', callback)
  }

  dispose() {
    this.emitter.emit('did-destroy')
    if (this.markers.size) {
      this.markers.forEach(marker => marker.destroy())
      this.markers.clear()
    }
    this.removeGutter()
    this.subscriptions.dispose()
    this.emitter.dispose()
    this.messages.clear()
  }
}
<commit_msg>:new: Add EditorLinter::calculateLineMessages<commit_after>'use babel'

import {Emitter, CompositeDisposable} from 'atom'
export default class EditorLinter {
  constructor(editor, isActive) {
    if (typeof editor !== 'object' || typeof editor.markBufferRange !== 'function') {
      throw new Error('Given editor is not really an editor')
    }

    this.editor = editor
    this.emitter = new Emitter()
    this.messages = new Set()
    this.markers = new WeakMap()
    this.gutter = null
    this.subscriptions = new CompositeDisposable

    this.subscriptions.add(atom.config.observe('linter.underlineIssues', underlineIssues =>
      this.underlineIssues = underlineIssues
    ))
    this.subscriptions.add(this.editor.onDidDestroy(() =>
      this.dispose()
    ))
    this.subscriptions.add(this.editor.onDidSave(() =>
      this.emitter.emit('should-lint', false)
    ))
    this.subscriptions.add(this.editor.onDidChangeCursorPosition(({oldBufferPosition, newBufferPosition}) => {
      if (newBufferPosition.row !== oldBufferPosition.row) {
        this.calculateLineMessages(newBufferPosition.row)
      }
      this.emitter.emit('should-update-bubble')
    }))
    this.subscriptions.add(atom.config.observe('linter.gutterEnabled', gutterEnabled => {
      this.gutterEnabled = gutterEnabled
      this.handleGutter()
    }))
    // Using onDidChange instead of observe here 'cause the same function is invoked above
    this.subscriptions.add(atom.config.onDidChange('linter.gutterPosition', () =>
      this.handleGutter()
    ))
    this.subscriptions.add(this.onDidMessageAdd(message => {
      if (!this.underlineIssues && !this.gutterEnabled) {
        return // No-Op
      }
      const marker = this.editor.markBufferRange(message.range, {invalidate: 'inside'})
      this.markers.set(message, marker)
      if (this.underlineIssues) {
        this.editor.decorateMarker(marker, {
          type: 'highlight',
          class: `linter-highlight ${message.class}`
        })
      }
      if (this.gutterEnabled) {
        const item = document.createElement('span')
        item.className = `linter-gutter linter-highlight ${message.class}`
        this.gutter.decorateMarker(marker, {
          class: 'linter-row',
          item
        })
      }
    }))
    this.subscriptions.add(this.onDidMessageDelete(message => {
      if (this.markers.has(message)) {
        this.markers.get(message).destroy()
        this.markers.delete(message)
      }
    }))

    // Atom invokes the onDidStopChanging callback immediately on Editor creation. So we wait a moment
    setImmediate(() => {
      this.subscriptions.add(this.editor.onDidStopChanging(() =>
        this.emitter.emit('should-lint', true)
      ))
    })

    this.active = isActive
  }

  set active(value) {
    value = Boolean(value)
    if (value !== this._active) {
      this._active = value
      if (this.messages.size) {
        this.messages.forEach(message => message.currentFile = value)
      }
    }
  }
  get active() {
    return this._active
  }

  handleGutter() {
    if (this.gutter !== null) {
      this.removeGutter()
    }
    if (this.gutterEnabled) {
      this.addGutter()
    }
  }

  addGutter() {
    const position = atom.config.get('linter.gutterPosition')
    this.gutter = this.editor.addGutter({
      name: 'linter',
      priority: position === 'Left' ? -100 : 100
    })
  }

  removeGutter() {
    if (this.gutter !== null) {
      try {
        // Atom throws when we try to remove a gutter container from a closed text editor
        this.gutter.destroy()
      } catch (err) {}
      this.gutter = null
    }
  }

  getMessages() {
    return this.messages
  }

  addMessage(message) {
    if (!this.messages.has(message)) {
      if (this.active) {
        message.currentFile = true
      }
      this.messages.add(message)
      this.emitter.emit('did-message-add', message)
      this.emitter.emit('did-message-change', {message, type: 'add'})
    }
  }

  deleteMessage(message) {
    if (this.messages.has(message)) {
      this.messages.delete(message)
      this.emitter.emit('did-message-delete', message)
      this.emitter.emit('did-message-change', {message, type: 'delete'})
    }
  }

  calculateLineMessages(row) {
    let count = 0
    this.messages.forEach(function(message) {
      if (message.currentLine = message.range && message.range.intersectsRow(row)) {
        count++
      }
    })
    this.emitter.emit('did-calculate-line-messages', count)
    return count
  }

  lint(onChange = false) {
    this.emitter.emit('should-lint', onChange)
  }

  onDidMessageAdd(callback) {
    return this.emitter.on('did-message-add', callback)
  }

  onDidMessageDelete(callback) {
    return this.emitter.on('did-message-delete', callback)
  }

  onDidMessageChange(callback) {
    return this.emitter.on('did-message-change', callback)
  }

  onDidCalculateLineMessages(callback) {
    return this.emitter.on('did-calculate-line-messages', callback)
  }

  onShouldUpdateBubble(callback) {
    return this.emitter.on('should-update-bubble', callback)
  }

  onShouldLint(callback) {
    return this.emitter.on('should-lint', callback)
  }

  onDidDestroy(callback) {
    return this.emitter.on('did-destroy', callback)
  }

  dispose() {
    this.emitter.emit('did-destroy')
    if (this.markers.size) {
      this.markers.forEach(marker => marker.destroy())
      this.markers.clear()
    }
    this.removeGutter()
    this.subscriptions.dispose()
    this.emitter.dispose()
    this.messages.clear()
  }
}
<|endoftext|>


<commit_before>"use strict";

var fs        = require("fs");
var path      = require("path");
var validator = require("express-validator");
var util      = require("util");

var DEBUG = false;

var conditionals = {};

function createConditional(name, func)
{
  if (!name || typeof name !== "string") {
    throw new Error("name argument invalid or missing");
  }
  if (!func || typeof func !== "function") {
    throw new Error("func argument invalid or missing");
  }
  if (conditionals[name]) {
    throw new Error("Trying to create a duplicate conditional '" + name + "'");
  }

  conditionals[name] = func;
}

function validateRequiredParameters (route)
{
  if (!route.path) {
    throw new Error("Missing path");
  }
  if (!route.method) {
    throw new Error("Missing method");
  }
  if (!route.respond) {
    throw new Error("Missing reponder");
  }
}

var HTTP_METHODS = [
  "GET",
  "POST",
  "PUT",
  "DELETE",
  "HEAD",
  "OPTIONS"
];

function validateMethod (method)
{
  if (HTTP_METHODS.indexOf(method) === -1)
    throw new Error("Invalid method " + method);
}

function DEFAULT_VALIDATOR (req, res)
{
  // Do nothing
}

function checkConditionals (route, req, res)
{
  var conds = route.conditions || [];
  var shouldRunRoute = true;

  conds.forEach(function (conditionalName) {
    if (!shouldRunRoute) return;

    if (!conditionals[conditionalName]) {
      throw new Error("Unexisting conditional '" + conditionalName + "'");
    }

    shouldRunRoute = conditionals[conditionalName](req, res);
  });

  return shouldRunRoute;
}

function buildHandler (route)
{
  var validate = route.validate || DEFAULT_VALIDATOR;

  return function routeHandler(req, res, next) {

    var shouldRun = checkConditionals(route, req, res);
    if (!shouldRun) {
      return next();
    }

    validate(req, res);

    // If they are validation errors, respond with it
    var errors = req.validationErrors();
    if (errors) {
      return res.json(400, { errors: errors });
    }

    if (DEBUG) {
      console.log("Responding with route  =>  %s %s", route.method.toUpperCase(), route.path);
    }

    // Else execute the route code
    route.respond(req, res, next);
  };
}

function _buildRoute (app, definition)
{
  var methodName = definition.method.toLowerCase();

  app[methodName].apply(
    app, [definition.path, buildHandler(definition)]
  );

  if (DEBUG) {
    console.log("Created route  =>  %s %s", definition.method, definition.path);
  }
}

function _validateRoute (definition)
{
  validateRequiredParameters(definition);
  validateMethod(definition.method);
}

function createRoutes (app, directory)
{
  directory = directory || __dirname;

  app.use(validator());

  var files = fs.readdirSync(directory);
  var excludes = ["index.js"];
  var definitions = [];

  files.forEach(function (file) {

    if (excludes.indexOf(file) !== -1)
      return;

    if (file.length > 0 && file[0] === '_')
      return;

    var realPath = path.resolve(directory, file);

    try {
      var definition = require(realPath);

      _validateRoute(definition);

      definitions.push(definition);
    }
    catch (error) {
      // Make error more tracable
      error.message = error.message + util.format(" in \"%s\"", realPath);
      throw error;
    }
  });

  definitions.sort(function (a, b) {
    return (b.conditions || []).length - (a.conditions || []).length;
  });

  definitions.forEach(function (definition) {
    _buildRoute(app, definition);
  });
}

function enableDebug ()
{
  DEBUG = true;
}

module.exports = {
  create             : createRoutes,
  createConditional  : createConditional,
  enableDebug        : enableDebug,
  _buildRoute        : _buildRoute,
  _checkConditionals : checkConditionals,
  _conditionals      : conditionals,
  _validateRoute     : _validateRoute
};
<commit_msg>Added check for files starting with '.'<commit_after>"use strict";

var fs        = require("fs");
var path      = require("path");
var validator = require("express-validator");
var util      = require("util");

var DEBUG = false;

var conditionals = {};

function createConditional(name, func)
{
  if (!name || typeof name !== "string") {
    throw new Error("name argument invalid or missing");
  }
  if (!func || typeof func !== "function") {
    throw new Error("func argument invalid or missing");
  }
  if (conditionals[name]) {
    throw new Error("Trying to create a duplicate conditional '" + name + "'");
  }

  conditionals[name] = func;
}

function validateRequiredParameters (route)
{
  if (!route.path) {
    throw new Error("Missing path");
  }
  if (!route.method) {
    throw new Error("Missing method");
  }
  if (!route.respond) {
    throw new Error("Missing reponder");
  }
}

var HTTP_METHODS = [
  "GET",
  "POST",
  "PUT",
  "DELETE",
  "HEAD",
  "OPTIONS"
];

function validateMethod (method)
{
  if (HTTP_METHODS.indexOf(method) === -1)
    throw new Error("Invalid method " + method);
}

function DEFAULT_VALIDATOR (req, res)
{
  // Do nothing
}

function checkConditionals (route, req, res)
{
  var conds = route.conditions || [];
  var shouldRunRoute = true;

  conds.forEach(function (conditionalName) {
    if (!shouldRunRoute) return;

    if (!conditionals[conditionalName]) {
      throw new Error("Unexisting conditional '" + conditionalName + "'");
    }

    shouldRunRoute = conditionals[conditionalName](req, res);
  });

  return shouldRunRoute;
}

function buildHandler (route)
{
  var validate = route.validate || DEFAULT_VALIDATOR;

  return function routeHandler(req, res, next) {

    var shouldRun = checkConditionals(route, req, res);
    if (!shouldRun) {
      return next();
    }

    validate(req, res);

    // If they are validation errors, respond with it
    var errors = req.validationErrors();
    if (errors) {
      return res.json(400, { errors: errors });
    }

    if (DEBUG) {
      console.log("Responding with route  =>  %s %s", route.method.toUpperCase(), route.path);
    }

    // Else execute the route code
    route.respond(req, res, next);
  };
}

function _buildRoute (app, definition)
{
  var methodName = definition.method.toLowerCase();

  app[methodName].apply(
    app, [definition.path, buildHandler(definition)]
  );

  if (DEBUG) {
    console.log("Created route  =>  %s %s", definition.method, definition.path);
  }
}

function _validateRoute (definition)
{
  validateRequiredParameters(definition);
  validateMethod(definition.method);
}

function createRoutes (app, directory)
{
  directory = directory || __dirname;

  app.use(validator());

  var files = fs.readdirSync(directory);
  var excludes = ["index.js"];
  var definitions = [];

  files.forEach(function (file) {

    if (excludes.indexOf(file) !== -1)
      return;

    if (file.length > 0 && file[0] === '_' && file[0] === '.')
      return;

    var realPath = path.resolve(directory, file);

    try {
      var definition = require(realPath);

      _validateRoute(definition);

      definitions.push(definition);
    }
    catch (error) {
      // Make error more tracable
      error.message = error.message + util.format(" in \"%s\"", realPath);
      throw error;
    }
  });

  definitions.sort(function (a, b) {
    return (b.conditions || []).length - (a.conditions || []).length;
  });

  definitions.forEach(function (definition) {
    _buildRoute(app, definition);
  });
}

function enableDebug ()
{
  DEBUG = true;
}

module.exports = {
  create             : createRoutes,
  createConditional  : createConditional,
  enableDebug        : enableDebug,
  _buildRoute        : _buildRoute,
  _checkConditionals : checkConditionals,
  _conditionals      : conditionals,
  _validateRoute     : _validateRoute
};
<|endoftext|>


<commit_before>'use strict';

var every          = Array.prototype.every
  , call           = Function.prototype.call
  , assertNotNull  = require('es5-ext/lib/assert-not-null')
  , isError        = require('es5-ext/lib/Error/is-error')
  , assertCallable = require('es5-ext/lib/Object/assert-callable')
  , create         = require('es5-ext/lib/Object/prototype/plain-create')
  , deferred       = require('../../deferred')
  , isPromise      = require('../../is-promise');

var proto = {
	iterate: function (value, index) {
		if (!this.cb && isError(value)) {
			this.d.resolve(value);
			return false;
		}
		++this.waiting;
		if (isPromise(value)) {
			if (isPromise(value = value.valueOf())) {
				value.end(this.processValue.bind(this, index), this.d.resolve);
			} else if (isError(value)) {
				this.d.resolve(value);
				return false;
			} else {
				return this.processValue(index, value);
			}
		} else {
			return this.processValue(index, value);
		}
		return true;
	},
	processValue: function self(index, value) {
		var d;
		if (this.d.promise._base.resolved) {
			return false;
		}
		if (this.cb) {
			if (!this.limit) {
				d = deferred();
				this.held.push(d.resolve);
				d.promise(self.bind(this, index, value));
				return true;
			}
			try {
				value = call.call(this.cb, this.context, value, index, this.list);
			} catch (e) {
				this.d.resolve(e);
				return false;
			}
			if (isPromise(value) && isPromise(value = value.valueOf())) {
				--this.limit;
				value.end(this.unhold.bind(this, index), this.d.resolve);
				return true;
			} else if (isError(value)) {
				this.d.resolve(value);
				return false;
			}
		}
		this.processResult(index, value);
		return true;
	},
	unhold: function (index, value) {
		this.processResult(index, value);
		if (!this.d.promise._base.resolved) {
			++this.limit;
			if (this.held.length) {
				this.held.shift()();
			}
		}
	},
	processResult: function (index, value) {
		if (this.d.promise._base.resolved) {
			return;
		}
		this.result[index] = value;
		if (!--this.waiting && this.initialized) {
			this.d.resolve(this.result);
		}
	}
};

module.exports = function (cb, thisArg, limit) {
	var result, iterator, d;
	assertNotNull(this);
	if (cb != null) {
		assertCallable(cb);
	}
	d = deferred();
	every.call(this, (iterator = create.call(proto, {
		d: d,
		cb: cb,
		held: [],
		limit: (limit >>> 0) || Infinity,
		list: this,
		context: thisArg,
		waiting: 0,
		result: new Array(this.length >>> 0)
	})).iterate, iterator);
	iterator.initialized = true;
	if (!iterator.waiting) {
		d.resolve(iterator.result);
	}
	return d.promise;
};
<commit_msg>Documentation<commit_after>// Promise aware Array's map

'use strict';

var every          = Array.prototype.every
  , call           = Function.prototype.call
  , assertNotNull  = require('es5-ext/lib/assert-not-null')
  , isError        = require('es5-ext/lib/Error/is-error')
  , assertCallable = require('es5-ext/lib/Object/assert-callable')
  , create         = require('es5-ext/lib/Object/prototype/plain-create')
  , deferred       = require('../../deferred')
  , isPromise      = require('../../is-promise');

var proto = {
	iterate: function (value, index) {
		if (!this.cb && isError(value)) {
			this.d.resolve(value);
			return false;
		}
		++this.waiting;
		if (isPromise(value)) {
			if (isPromise(value = value.valueOf())) {
				value.end(this.processValue.bind(this, index), this.d.resolve);
			} else if (isError(value)) {
				this.d.resolve(value);
				return false;
			} else {
				return this.processValue(index, value);
			}
		} else {
			return this.processValue(index, value);
		}
		return true;
	},
	processValue: function self(index, value) {
		var d;
		if (this.d.promise._base.resolved) {
			return false;
		}
		if (this.cb) {
			if (!this.limit) {
				d = deferred();
				this.held.push(d.resolve);
				d.promise(self.bind(this, index, value));
				return true;
			}
			try {
				value = call.call(this.cb, this.context, value, index, this.list);
			} catch (e) {
				this.d.resolve(e);
				return false;
			}
			if (isPromise(value) && isPromise(value = value.valueOf())) {
				--this.limit;
				value.end(this.unhold.bind(this, index), this.d.resolve);
				return true;
			} else if (isError(value)) {
				this.d.resolve(value);
				return false;
			}
		}
		this.processResult(index, value);
		return true;
	},
	unhold: function (index, value) {
		this.processResult(index, value);
		if (!this.d.promise._base.resolved) {
			++this.limit;
			if (this.held.length) {
				this.held.shift()();
			}
		}
	},
	processResult: function (index, value) {
		if (this.d.promise._base.resolved) {
			return;
		}
		this.result[index] = value;
		if (!--this.waiting && this.initialized) {
			this.d.resolve(this.result);
		}
	}
};

module.exports = function (cb, thisArg, limit) {
	var result, iterator, d;
	assertNotNull(this);
	if (cb != null) {
		assertCallable(cb);
	}
	d = deferred();
	every.call(this, (iterator = create.call(proto, {
		d: d,
		cb: cb,
		held: [],
		limit: (limit >>> 0) || Infinity,
		list: this,
		context: thisArg,
		waiting: 0,
		result: new Array(this.length >>> 0)
	})).iterate, iterator);
	iterator.initialized = true;
	if (!iterator.waiting) {
		d.resolve(iterator.result);
	}
	return d.promise;
};
<|endoftext|>


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

exports.middleware = function(app) {
  // This flag is used to tell the middleware we create that the server wants
  // to stop, so we do not allow anymore connections. This is done for all new
  // connections for us by Node, but we need to handle the connections that are
  // using the Keep-Alive header to stay on.
  app.set('graceful_exit', false)

  return function(req, res, next) {
    // We override res.end so that we can differentiate between connections
    // that are really in the middle of execution and the ones that are just
    // being kept alive.
    var rEnd = res.end
    res.end = function(chunk, encoding) {
      // And this connection is done
      req.connection.graceful = {processing: false}

      // Go back to the real res.end
      res.end = rEnd
      res.end(chunk, encoding)
    }

    // Sorry Keep-Alive connections, but we need to part ways
    if (app.settings.graceful_exit === true) {
      res.setHeader('Connection', 'close')
      res.send(502, 'Server is exiting')
      return
    }

    // And we are in the middle of something real
    req.connection.graceful = {processing: true}

    next()
  }
}

exports.gracefulExitHandler = function(app, server, _options) {
  // Get the options set up
  if (!_options) _options = {}
  var options = _.defaults(_options, {
      log               : false
    , logger            : console.log
    , suicideTimeout    : 3*60*1000 // 3m in ms
  })

  function logger(str) {
    if (options.log)
      options.logger(str)
  }

  logger('Closing down the server')

  // Let everything know that we wish to exit gracefully
  app.set('graceful_exit', true)

  // Time to stop accepting new connections
  server.close(function() {
    // Everything was closed successfully, mission accomplished!
    logger('All connections done, stopping process')
    process.exit(0)
  })

  // Disconnect all the socket.io clients
  if (options.socketio) {
    options.socketio.sockets.clients().forEach(function(socket) {
      logger('Killing socketio socket')
      socket.disconnect()
    })
  }

  // If after an acceptable time limit is reached and we still have some
  // connections lingering around for some reason, just die... we tried to
  // be graceful, but failed.
  setTimeout(function() {
    logger('Exiting process with some open connections left')
    process.exit(1)
  }, options.suicideTimeout)
}
<commit_msg>Let the keep-alive connections continue on, but give them a 1ms idle timeout to get them out of here<commit_after>
var _ = require('underscore')

exports.middleware = function(app) {
  // This flag is used to tell the middleware we create that the server wants
  // to stop, so we do not allow anymore connections. This is done for all new
  // connections for us by Node, but we need to handle the connections that are
  // using the Keep-Alive header to stay on.
  app.set('graceful_exit', false)

  return function(req, res, next) {
    // Sorry Keep-Alive connections, but we need to part ways
    if (app.settings.graceful_exit === true) {
      req.connection.setTimeout(1)
    }

    next()
  }
}

exports.gracefulExitHandler = function(app, server, _options) {
  // Get the options set up
  if (!_options) _options = {}
  var options = _.defaults(_options, {
      log               : false
    , logger            : console.log
    , suicideTimeout    : 3*60*1000 // 3m in ms
  })

  function logger(str) {
    if (options.log)
      options.logger(str)
  }

  logger('Closing down the server')

  // Let everything know that we wish to exit gracefully
  app.set('graceful_exit', true)

  // Time to stop accepting new connections
  server.close(function() {
    // Everything was closed successfully, mission accomplished!
    logger('All connections done, stopping process')
    process.exit(0)
  })

  // Disconnect all the socket.io clients
  if (options.socketio) {
    options.socketio.sockets.clients().forEach(function(socket) {
      logger('Killing socketio socket')
      socket.disconnect()
    })
  }

  // If after an acceptable time limit is reached and we still have some
  // connections lingering around for some reason, just die... we tried to
  // be graceful, but failed.
  setTimeout(function() {
    logger('Exiting process with some open connections left')
    process.exit(1)
  }, options.suicideTimeout)
}
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var VertexWrapper = require('./vertex-wrapper');
var EdgeWrapper = require('./edge-wrapper');
var Q = require('q');

var assert = require('assert');   // TODO: remove asserts

var GraphWrapper = module.exports = function (gremlin, graph) {
  assert.ok(gremlin);
  assert.ok(graph);
  this.gremlin = gremlin;
  this.graph = graph;

  // re-export some of gremlin's utility functions as
  // part of the graph wrapper instance
  this.java = gremlin.java;
  this.ClassTypes = gremlin.ClassTypes;
  this.Tokens = gremlin.Tokens;
  this.Compare = gremlin.Compare;
  this.Contains = gremlin.Contains;
  this.Direction = gremlin.Direction;
  this.ArrayList = gremlin.ArrayList;
  this.HashMap = gremlin.HashMap;
//   this.Table = gremlin.Table;    // No TK3 equivalent?
  this.Tree = gremlin.Tree;
  this.isType = gremlin.isType.bind(gremlin);
  this.toList = gremlin.toList.bind(gremlin);
  this.toListSync = gremlin.toListSync.bind(gremlin);
};

GraphWrapper.prototype.loadGraphSONSync = function (filename) {
  var FileInputStream = this.java.import('java.io.FileInputStream');
  var stream = new FileInputStream(filename);
  var builder = this.java.callStaticMethodSync('com.tinkerpop.gremlin.structure.io.graphson.GraphSONReader', 'build');
  var reader = builder.createSync();
  reader.readGraphSync(stream, this.graph);
};

GraphWrapper.prototype.saveGraphSONSync = function (filename) {
  var FileOutputStream = this.java.import('java.io.FileOutputStream');
  var stream = new FileOutputStream(filename);
  var builder = this.java.callStaticMethodSync('com.tinkerpop.gremlin.structure.io.graphson.GraphSONWriter', 'build');
  var writer = builder.createSync();
  writer.writeGraphSync(stream, this.graph);
};

GraphWrapper.prototype._supportsTransactions = function () {
  var features = this.graph.featuresSync();
  var graphFeatures = features.graphSync();
  return graphFeatures.supportsThreadedTransactionsSync();
};

GraphWrapper.prototype._getTransaction = function () {
  // Transactions in TransactionalGraph's are often, by default, bound against the
  // executing thread (e.g. as a ThreadLocal variable). This behavior is not very
  // helpful in JavaScript because while the main execution is in fact performed
  // on a single thread, often a pool of threads exist to service asynchronous tasks,
  // making our tasks often operate on an incorrect transaction instance.
  //
  // Due to this, we try and avoid this default thread-bound functionality and manage
  // our own life-cycle if the supplied graph instance provides the interface to create
  // a transaction independent of the executing thread.
  //
  if (this.graph.txn) {
    return this.graph.txn;
  }

  var supportsTransactions = this._supportsTransactions();
  if (!supportsTransactions) {
    // When transactions are not supported, we return the graph itself.
    return this.graph;
  }

  // TODO: The following introduces a new member this.graph.tx, which seems to be new with TK3.
  // Given the tx, we can create a Graph interface that is a transaction that can be executed
  // across multiple threads. Other methods involving transactions almost certainly need to
  // be updated.
  this.graph.tx = this.graph.txSync();
  this.graph.txn = this.graph.tx.createSync();
  return this.graph.txn;
};

GraphWrapper.prototype._clearTransaction = function () {
  if (this.graph.txn) {
    this.graph.tx = null;
    this.graph.txn = null;
  }
};

// com.tinkerpop.blueprints.Graph interface
GraphWrapper.prototype.addVertex = function (properties, callback) {
  var gremlin = this.gremlin;
  var txn = this._getTransaction();

  // In TP2, the first argument as an id, which was often specified as null.
  // In TP3, the first argument is an simple object with initial properties.
  // These asserts should help catch code that needs to be updated for TP3.
  assert(_.isObject(properties));
  assert(!_.isFunction(properties));

  var deferred = Q.defer();

  txn.addVertex(gremlin.propertiesToVarArgs(properties), function (err, v) {
    if (err)
      return deferred.reject(err);
    else
      return deferred.resolve(gremlin.wrapVertex(v));
  });

  return deferred.promise.nodeify(callback);
};

GraphWrapper.prototype.getVertex = function (id, callback) {
  var gremlin = this.gremlin;
  var txn = this._getTransaction();

  return Q.nbind(txn.vertexIterator, txn)([id])
    .then(function (vertexIterator) {
      return new Q(vertexIterator.hasNextSync() ? gremlin.wrapVertex(vertexIterator.nextSync()) : null);
    })
    .nodeify(callback);
};

// In Tinkerpop3, instead of graph.removeVertex(v), just do v.remove().
// GraphWrapper.prototype.removeVertex = function (vertex, callback) {
//   var txn = this._getTransaction();
//
//   if (!(vertex instanceof VertexWrapper)) {
//     throw new TypeError('vertex must be an instance of VertexWrapper');
//   }
//
//   return Q.nbind(txn.removeVertex, txn)(vertex.unwrap())
//     .nodeify(callback);
// };

GraphWrapper.prototype.addEdge = function (id, outVertex, inVertex, label, callback) {
  var gremlin = this.gremlin;
  var txn = this._getTransaction();

  if (!(outVertex instanceof VertexWrapper)) {
    throw new TypeError('outVertex must be an instance of VertexWrapper');
  }
  if (!(inVertex instanceof VertexWrapper)) {
    throw new TypeError('inVertex must be an instance of VertexWrapper');
  }

  return Q.nbind(txn.addEdge, txn)(id, outVertex.unwrap(), inVertex.unwrap(), label)
    .then(function (e) { return new Q(gremlin.wrapEdge(e)); })
    .nodeify(callback);
};

GraphWrapper.prototype.getEdge = function (id, callback) {
  var gremlin = this.gremlin;
  var txn = this._getTransaction();

  return Q.nbind(txn.edgeIterator, txn)([id])
    .then(function (edgeIterator) {
      return new Q(edgeIterator.hasNextSync() ? gremlin.wrapEdge(edgeIterator.nextSync()) : null);
    })
    .nodeify(callback);
};

GraphWrapper.prototype.removeEdge = function (edge, callback) {
  var txn = this._getTransaction();

  if (!(edge instanceof EdgeWrapper)) {
    throw new TypeError('edge must be an instance of EdgeWrapper');
  }

  return Q.nbind(txn.removeEdge, txn)(edge.unwrap())
    .nodeify(callback);
};

GraphWrapper.prototype.query = function () {
  var txn = this._getTransaction();
  return this.gremlin.wrapQuery(txn.querySync());
};

GraphWrapper.prototype.requiresThreadedTransactions = function () {
  var supportsTransactions = this._supportsTransactions();
  if (!this._supportsTransactions()) {
    throw new Error('Graph instance must support threaded transactions.');
  }
};

GraphWrapper.prototype.newTransaction = function () {
  this.requiresThreadedTransactions();
  var txn = this.graph.txSync();
  return this.gremlin.wrap(txn);
};

GraphWrapper.prototype.commit = function (callback) {
  this.requiresThreadedTransactions();
  var txn = this._getTransaction();
  this._clearTransaction();
  txn.commit(callback);
};

GraphWrapper.prototype.rollback = function (callback) {
  this.requiresThreadedTransactions();
  var txn = this._getTransaction();
  this._clearTransaction();
  txn.rollback(callback);
};

GraphWrapper.prototype.shutdown = function (callback) {
  this.requiresThreadedTransactions();
  var txn = this._getTransaction();
  this._clearTransaction();
  txn.shutdown(callback);
};

// gremlin shell extensions for the graph object
GraphWrapper.prototype._ = function () {
  var txn = this._getTransaction();
  var traversal = this.gremlin.wrapTraversal(txn);
  traversal.traversal._Sync();
  return traversal;
};

GraphWrapper.prototype.start = function (start) {
  var txn = this._getTransaction();
  var traversal = this.gremlin.wrapTraversal(txn);
  // conditionally unwrap, we may be being passed a Java list instead
  // of one of our wrapper JavaScript objects
  if (start.unwrap) {
    start = start.unwrap();
  }
  return traversal.start(start);
};

// TODO: In TK2, we could say g.V(key, value).
// In TK3, we have to say g.V().has(key, value).
// We could restore the behavior by checking if args.length>0, and automatically
// adding the has step.
GraphWrapper.prototype.V = function () {
  var args = Array.prototype.slice.call(arguments);
  assert(args.length === 0);
  var txn = this._getTransaction();
  return this.gremlin.wrapTraversal(txn.VSync());
};

// TODO: Likewise, g.E(key, value) is currently disallowed, but could be supported.
// See above for g.V()
GraphWrapper.prototype.E = function () {
  var args = Array.prototype.slice.call(arguments);
  assert(args.length === 0);
  var txn = this._getTransaction();
  return this.gremlin.wrapTraversal(txn.ESync());
};

GraphWrapper.prototype.v = function (id, callback) {
  var txn = this._getTransaction();
  var gremlin = this.gremlin;

  return Q.nbind(txn.v, txn)(id)
    .then(function (v) { return gremlin.wrapVertex(v); })
    .catch(function (err) {
      if (err.toString().match(/java.util.NoSuchElementException/))
        return undefined;
      else
        throw err;
    })
    .nodeify(callback);
};

GraphWrapper.prototype.e = function () {
  var txn = this._getTransaction();
  var gremlin = this.gremlin;
  var argPair = gremlin.extractArguments(Array.prototype.slice.call(arguments));
  if (argPair.args.length === 0)
    throw new Error('e() requires at least one argument.');

  return Q.all(argPair.args.map(function (id) {
      return Q.nbind(txn.getEdge, txn)(id);
    }))
    .then(function (edges) {
      var list = new gremlin.ArrayList();
      edges.forEach(function (e) {
        list.addSync(e);
      });
      return new Q(list);
    })
    .then(function (list) {
      return new Q(gremlin.wrapTraversal(list.iteratorSync()));
    })
    .nodeify(argPair.callback);
};

GraphWrapper.prototype.toString = function (callback) {
  return Q.nbind(this.graph.toString, this.graph)().nodeify(callback);
};

GraphWrapper.prototype.toStringSync = function () {
  return this.graph.toStringSync();
};
<commit_msg>Fix GraphWrapper.getEdge to throw exception when the edge does not exist<commit_after>'use strict';

var _ = require('lodash');
var VertexWrapper = require('./vertex-wrapper');
var EdgeWrapper = require('./edge-wrapper');
var Q = require('q');

var assert = require('assert');   // TODO: remove asserts

var GraphWrapper = module.exports = function (gremlin, graph) {
  assert.ok(gremlin);
  assert.ok(graph);
  this.gremlin = gremlin;
  this.graph = graph;

  // re-export some of gremlin's utility functions as
  // part of the graph wrapper instance
  this.java = gremlin.java;
  this.ClassTypes = gremlin.ClassTypes;
  this.Tokens = gremlin.Tokens;
  this.Compare = gremlin.Compare;
  this.Contains = gremlin.Contains;
  this.Direction = gremlin.Direction;
  this.ArrayList = gremlin.ArrayList;
  this.HashMap = gremlin.HashMap;
//   this.Table = gremlin.Table;    // No TK3 equivalent?
  this.Tree = gremlin.Tree;
  this.isType = gremlin.isType.bind(gremlin);
  this.toList = gremlin.toList.bind(gremlin);
  this.toListSync = gremlin.toListSync.bind(gremlin);
};

GraphWrapper.prototype.loadGraphSONSync = function (filename) {
  var FileInputStream = this.java.import('java.io.FileInputStream');
  var stream = new FileInputStream(filename);
  var builder = this.java.callStaticMethodSync('com.tinkerpop.gremlin.structure.io.graphson.GraphSONReader', 'build');
  var reader = builder.createSync();
  reader.readGraphSync(stream, this.graph);
};

GraphWrapper.prototype.saveGraphSONSync = function (filename) {
  var FileOutputStream = this.java.import('java.io.FileOutputStream');
  var stream = new FileOutputStream(filename);
  var builder = this.java.callStaticMethodSync('com.tinkerpop.gremlin.structure.io.graphson.GraphSONWriter', 'build');
  var writer = builder.createSync();
  writer.writeGraphSync(stream, this.graph);
};

GraphWrapper.prototype._supportsTransactions = function () {
  var features = this.graph.featuresSync();
  var graphFeatures = features.graphSync();
  return graphFeatures.supportsThreadedTransactionsSync();
};

GraphWrapper.prototype._getTransaction = function () {
  // Transactions in TransactionalGraph's are often, by default, bound against the
  // executing thread (e.g. as a ThreadLocal variable). This behavior is not very
  // helpful in JavaScript because while the main execution is in fact performed
  // on a single thread, often a pool of threads exist to service asynchronous tasks,
  // making our tasks often operate on an incorrect transaction instance.
  //
  // Due to this, we try and avoid this default thread-bound functionality and manage
  // our own life-cycle if the supplied graph instance provides the interface to create
  // a transaction independent of the executing thread.
  //
  if (this.graph.txn) {
    return this.graph.txn;
  }

  var supportsTransactions = this._supportsTransactions();
  if (!supportsTransactions) {
    // When transactions are not supported, we return the graph itself.
    return this.graph;
  }

  // TODO: The following introduces a new member this.graph.tx, which seems to be new with TK3.
  // Given the tx, we can create a Graph interface that is a transaction that can be executed
  // across multiple threads. Other methods involving transactions almost certainly need to
  // be updated.
  this.graph.tx = this.graph.txSync();
  this.graph.txn = this.graph.tx.createSync();
  return this.graph.txn;
};

GraphWrapper.prototype._clearTransaction = function () {
  if (this.graph.txn) {
    this.graph.tx = null;
    this.graph.txn = null;
  }
};

// com.tinkerpop.blueprints.Graph interface
GraphWrapper.prototype.addVertex = function (properties, callback) {
  var gremlin = this.gremlin;
  var txn = this._getTransaction();

  // In TP2, the first argument as an id, which was often specified as null.
  // In TP3, the first argument is an simple object with initial properties.
  // These asserts should help catch code that needs to be updated for TP3.
  assert(_.isObject(properties));
  assert(!_.isFunction(properties));

  var deferred = Q.defer();

  txn.addVertex(gremlin.propertiesToVarArgs(properties), function (err, v) {
    if (err)
      return deferred.reject(err);
    else
      return deferred.resolve(gremlin.wrapVertex(v));
  });

  return deferred.promise.nodeify(callback);
};

GraphWrapper.prototype.getVertex = function (id, callback) {
  var gremlin = this.gremlin;
  var txn = this._getTransaction();

  return Q.nbind(txn.vertexIterator, txn)([id])
    .then(function (vertexIterator) {
      return new Q(vertexIterator.hasNextSync() ? gremlin.wrapVertex(vertexIterator.nextSync()) : null);
    })
    .nodeify(callback);
};

// In Tinkerpop3, instead of graph.removeVertex(v), just do v.remove().
// GraphWrapper.prototype.removeVertex = function (vertex, callback) {
//   var txn = this._getTransaction();
//
//   if (!(vertex instanceof VertexWrapper)) {
//     throw new TypeError('vertex must be an instance of VertexWrapper');
//   }
//
//   return Q.nbind(txn.removeVertex, txn)(vertex.unwrap())
//     .nodeify(callback);
// };

GraphWrapper.prototype.addEdge = function (id, outVertex, inVertex, label, callback) {
  var gremlin = this.gremlin;
  var txn = this._getTransaction();

  if (!(outVertex instanceof VertexWrapper)) {
    throw new TypeError('outVertex must be an instance of VertexWrapper');
  }
  if (!(inVertex instanceof VertexWrapper)) {
    throw new TypeError('inVertex must be an instance of VertexWrapper');
  }

  return Q.nbind(txn.addEdge, txn)(id, outVertex.unwrap(), inVertex.unwrap(), label)
    .then(function (e) { return new Q(gremlin.wrapEdge(e)); })
    .nodeify(callback);
};

GraphWrapper.prototype.getEdge = function (id, callback) {
  var gremlin = this.gremlin;
  var txn = this._getTransaction();

  return Q.nbind(txn.edgeIterator, txn)([id])
    .then(function (edgeIterator) {
      if (edgeIterator.hasNextSync()) {
        return gremlin.wrapEdge(edgeIterator.nextSync());
      } else {
        throw new Error('The edge with id ' + id + ' of type Integer does not exist in the graph');
      }
    })
    .nodeify(callback);
};

GraphWrapper.prototype.removeEdge = function (edge, callback) {
  var txn = this._getTransaction();

  if (!(edge instanceof EdgeWrapper)) {
    throw new TypeError('edge must be an instance of EdgeWrapper');
  }

  return Q.nbind(txn.removeEdge, txn)(edge.unwrap())
    .nodeify(callback);
};

GraphWrapper.prototype.query = function () {
  var txn = this._getTransaction();
  return this.gremlin.wrapQuery(txn.querySync());
};

GraphWrapper.prototype.requiresThreadedTransactions = function () {
  var supportsTransactions = this._supportsTransactions();
  if (!this._supportsTransactions()) {
    throw new Error('Graph instance must support threaded transactions.');
  }
};

GraphWrapper.prototype.newTransaction = function () {
  this.requiresThreadedTransactions();
  var txn = this.graph.txSync();
  return this.gremlin.wrap(txn);
};

GraphWrapper.prototype.commit = function (callback) {
  this.requiresThreadedTransactions();
  var txn = this._getTransaction();
  this._clearTransaction();
  txn.commit(callback);
};

GraphWrapper.prototype.rollback = function (callback) {
  this.requiresThreadedTransactions();
  var txn = this._getTransaction();
  this._clearTransaction();
  txn.rollback(callback);
};

GraphWrapper.prototype.shutdown = function (callback) {
  this.requiresThreadedTransactions();
  var txn = this._getTransaction();
  this._clearTransaction();
  txn.shutdown(callback);
};

// gremlin shell extensions for the graph object
GraphWrapper.prototype._ = function () {
  var txn = this._getTransaction();
  var traversal = this.gremlin.wrapTraversal(txn);
  traversal.traversal._Sync();
  return traversal;
};

GraphWrapper.prototype.start = function (start) {
  var txn = this._getTransaction();
  var traversal = this.gremlin.wrapTraversal(txn);
  // conditionally unwrap, we may be being passed a Java list instead
  // of one of our wrapper JavaScript objects
  if (start.unwrap) {
    start = start.unwrap();
  }
  return traversal.start(start);
};

// TODO: In TK2, we could say g.V(key, value).
// In TK3, we have to say g.V().has(key, value).
// We could restore the behavior by checking if args.length>0, and automatically
// adding the has step.
GraphWrapper.prototype.V = function () {
  var args = Array.prototype.slice.call(arguments);
  assert(args.length === 0);
  var txn = this._getTransaction();
  return this.gremlin.wrapTraversal(txn.VSync());
};

// TODO: Likewise, g.E(key, value) is currently disallowed, but could be supported.
// See above for g.V()
GraphWrapper.prototype.E = function () {
  var args = Array.prototype.slice.call(arguments);
  assert(args.length === 0);
  var txn = this._getTransaction();
  return this.gremlin.wrapTraversal(txn.ESync());
};

GraphWrapper.prototype.v = function (id, callback) {
  var txn = this._getTransaction();
  var gremlin = this.gremlin;

  return Q.nbind(txn.v, txn)(id)
    .then(function (v) { return gremlin.wrapVertex(v); })
    .catch(function (err) {
      if (err.toString().match(/java.util.NoSuchElementException/))
        return undefined;
      else
        throw err;
    })
    .nodeify(callback);
};

GraphWrapper.prototype.e = function () {
  var txn = this._getTransaction();
  var gremlin = this.gremlin;
  var argPair = gremlin.extractArguments(Array.prototype.slice.call(arguments));
  if (argPair.args.length === 0)
    throw new Error('e() requires at least one argument.');

  return Q.all(argPair.args.map(function (id) {
      return Q.nbind(txn.getEdge, txn)(id);
    }))
    .then(function (edges) {
      var list = new gremlin.ArrayList();
      edges.forEach(function (e) {
        list.addSync(e);
      });
      return new Q(list);
    })
    .then(function (list) {
      return new Q(gremlin.wrapTraversal(list.iteratorSync()));
    })
    .nodeify(argPair.callback);
};

GraphWrapper.prototype.toString = function (callback) {
  return Q.nbind(this.graph.toString, this.graph)().nodeify(callback);
};

GraphWrapper.prototype.toStringSync = function () {
  return this.graph.toStringSync();
};
<|endoftext|>


<commit_before>"use strict";
var inherits = require('util').inherits;
var valid = require('valid');
var errors = require('../errors');
var ValidatingHandler = require('./core/validating_handler');

var UnsupportedMediaType = errors.UnsupportedMediaType;
var ParseError = errors.ParseError;

var validators = valid.validators;
var any = validators.any;
var isNull = validators.isNull;

var Data = function (dataSpec) {
	ValidatingHandler.call(this, dataSpec);
};
inherits(Data, ValidatingHandler);

Data.prototype.name = 'Data';
Data.prototype.supportedMediaTypes = ['application/json'];

Data.prototype.handleRequest = function (ctx) {
	var req = ctx.req;
	if (ctx.isHttp) {
		if (req.method == 'GET') {
			this.handleRequestWithBody(ctx, req.query.body);
		}
		else {
			var mediaType = req.mediaType;
			if (mediaType && this.supportedMediaTypes.indexOf(mediaType) == -1) {
				ctx.error(new UnsupportedMediaType(this.supportedMediaTypes));
			}
			else {
				var self = this;
				var limit = ctx.mechanicsData.bodyMaxSize;
				ctx.mechanics.collectBody(req, limit, function (err, buffer) {
					if (err != null) {
						// drop connection on RequestEntityTooLarge (unless in debug)
						if (!ctx.isDebug && errors.WebError.isWebError(err, 413)) {
							req.destroy();
							// force logging to simplify diagnostics
							// because client will not get response at all
							ctx.logError(err, 'warning', { force: true });
							ctx.done();
						}
						else {
							ctx.error(err);
						}
					} else {
						var body = null;
						if (buffer) {
							body = buffer.toString('utf-8');
						}
						self.handleRequestWithBody(ctx, body);
					}
				});
			}
		}
	}
	else {
		this.handleRequestWithBody(ctx, req.body);
	}
};

Data.prototype.handleRequestWithBody = function (ctx, body) {
	var data;
	var err = null;
	if (!ctx.isHttp && ctx.req.hasData) {
		data = ctx.req.data;
	}
	else if (body) {
		try {
			data = JSON.parse(body);
		}
		catch (parseErr) {
			err = new ParseError(parseErr);
		}
	}

	if (!err) {
		err = this.validate(ctx, data);
		if (!err) {
			ctx.data = data;
			ctx.next();
		}
	}

	if (err) {
		ctx.error(err);
	}
};

Data.data = function (dataSpec) {
	return new Data(dataSpec);
};

Object.defineProperties(Data.data, {
	any: {
		get: function () {
			return Data.data(any);
		}
	},
	none: {
		get: function () {
			return Data.data(isNull);
		}
	}
});

module.exports = Data;
<commit_msg>data.str introduced<commit_after>"use strict";
var inherits = require('util').inherits;
var valid = require('valid');
var errors = require('../errors');
var ValidatingHandler = require('./core/validating_handler');

var UnsupportedMediaType = errors.UnsupportedMediaType;
var ParseError = errors.ParseError;

var validators = valid.validators;
var any = validators.any;
var isNull = validators.isNull;
var str = validators.str;

var Data = function (dataSpec) {
	ValidatingHandler.call(this, dataSpec);
};
inherits(Data, ValidatingHandler);

Data.prototype.name = 'Data';
Data.prototype.supportedMediaTypes = ['application/json'];

Data.prototype.handleRequest = function (ctx) {
	var req = ctx.req;
	if (ctx.isHttp) {
		if (req.method == 'GET') {
			this.handleRequestWithBody(ctx, req.query.body);
		}
		else {
			var mediaType = req.mediaType;
			if (mediaType && this.supportedMediaTypes.indexOf(mediaType) == -1) {
				ctx.error(new UnsupportedMediaType(this.supportedMediaTypes));
			}
			else {
				var self = this;
				var limit = ctx.mechanicsData.bodyMaxSize;
				ctx.mechanics.collectBody(req, limit, function (err, buffer) {
					if (err != null) {
						// drop connection on RequestEntityTooLarge (unless in debug)
						if (!ctx.isDebug && errors.WebError.isWebError(err, 413)) {
							req.destroy();
							// force logging to simplify diagnostics
							// because client will not get response at all
							ctx.logError(err, 'warning', { force: true });
							ctx.done();
						}
						else {
							ctx.error(err);
						}
					} else {
						var body = null;
						if (buffer) {
							body = buffer.toString('utf-8');
						}
						self.handleRequestWithBody(ctx, body);
					}
				});
			}
		}
	}
	else {
		this.handleRequestWithBody(ctx, req.body);
	}
};

Data.prototype.handleRequestWithBody = function (ctx, body) {
	var data;
	var err = null;
	if (!ctx.isHttp && ctx.req.hasData) {
		data = ctx.req.data;
	}
	else if (body) {
		try {
			data = JSON.parse(body);
		}
		catch (parseErr) {
			err = new ParseError(parseErr);
		}
	}

	if (!err) {
		err = this.validate(ctx, data);
		if (!err) {
			ctx.data = data;
			ctx.next();
		}
	}

	if (err) {
		ctx.error(err);
	}
};

Data.data = function (dataSpec) {
	return new Data(dataSpec);
};

Object.defineProperties(Data.data, {
	any: {
		get: function () {
			return Data.data(any);
		}
	},
	none: {
		get: function () {
			return Data.data(isNull);
		}
	},
	str: {
		get: function () {
			return Data.data(str);
		}
	}
});

module.exports = Data;
<|endoftext|>


<commit_before>const queryFields = [ 'sort', 'fields', 'offset', 'limit' ];

const toMongo = (query) => {
  const { sort='-_id', fields, offset=0, limit=10 } = query;
  for (let field of queryFields) delete query[field];
  const mongoFilters = {
    sort: parseSort(sort),
    offset,
    limit,
    query: parseResQuery(query)
  };
  if (fields != null) {
    mongoFilters.fields = fields;
  }
  return mongoFilters;
};

const parseSort = (sortQuery) => {
  const sort = [].concat(sortQuery).reduce((sort, query) =>
    Object.assign(
      sort,
        query[0] == '-' ? { [query.substring(1)] : -1 } :
        { [query] : 1 }
    ),
    {}
  );
  return sort;
};

const assignParsedQueryField = (queryObject, symbol, operator, query, queryValue) => {
  let [fieldName, value] = query.split(symbol);

  if (value == '') {
    operator += 'e';
    value = queryValue;
  }

  if (value == '') {
    throw new Error(`query ${query} with no value`);
  }

  if (!isNaN(+value)) {
    value = +value;
  }

  if (queryObject[fieldName] == null) {
    queryObject[fieldName] = {};
  }

  Object.assign(queryObject[fieldName], { [operator]: value });
};

const parseResQuery = (resQuery) => {
  const query = {};
  for (let qf in resQuery) {
    if (qf.indexOf('>') > -1) {
      assignParsedQueryField(query, '>', '$gt', qf, resQuery[qf]);
    } else if (qf.indexOf('<') > -1) {
      assignParsedQueryField(query, '<', '$lt', qf, resQuery[qf]);
    } else if (qf.indexOf('~') > -1) {
      let pattern = resQuery[qf];
      let flags;
      if (pattern[0] == '/') {
        const parts = pattern.split('/');
        // has flags
        if (parts.length == 3) {
          flags = parts[2];
          pattern = parts[1];
        }
      }
      query[qf.split('~')[0]] = new RegExp(pattern, flags);
    } else {
      query[qf] = resQuery[qf];
    }
  }
  if (Object.keys(query).length > 0) {
    return query;
  }
};

module.exports = { toMongo };
<commit_msg>trimming sort property<commit_after>const queryFields = [ 'sort', 'fields', 'offset', 'limit' ];

const toMongo = (query) => {
  const { sort='-_id', fields, offset=0, limit=10 } = query;
  for (let field of queryFields) delete query[field];
  const mongoFilters = {
    sort: parseSort(sort),
    offset,
    limit,
    query: parseResQuery(query)
  };
  if (fields != null) {
    mongoFilters.fields = fields;
  }
  return mongoFilters;
};

const parseSort = (sortQuery) =>
  [].concat(sortQuery).reduce((sort, query) => {
    query = query.trim();
    return Object.assign(
      sort,
      (query[0] == '-' ? { [query.substring(1)] : -1 } :
       query[0] == '+' ? { [query.substring(1)] :  1 } :
                         { [query] : 1 })
    )},
    {}
  );

const assignParsedQueryField = (queryObject, symbol, operator, query, queryValue) => {
  let [fieldName, value] = query.split(symbol);

  if (value == '') {
    operator += 'e';
    value = queryValue;
  }

  if (value == '') {
    throw new Error(`query ${query} with no value`);
  }

  if (!isNaN(+value)) {
    value = +value;
  }

  if (queryObject[fieldName] == null) {
    queryObject[fieldName] = {};
  }

  Object.assign(queryObject[fieldName], { [operator]: value });
};

const parseResQuery = (resQuery) => {
  const query = {};
  for (let qf in resQuery) {
    if (qf.indexOf('>') > -1) {
      assignParsedQueryField(query, '>', '$gt', qf, resQuery[qf]);
    } else if (qf.indexOf('<') > -1) {
      assignParsedQueryField(query, '<', '$lt', qf, resQuery[qf]);
    } else if (qf.indexOf('~') > -1) {
      let pattern = resQuery[qf];
      let flags;
      if (pattern[0] == '/') {
        const parts = pattern.split('/');
        // has flags
        if (parts.length == 3) {
          flags = parts[2];
          pattern = parts[1];
        }
      }
      query[qf.split('~')[0]] = new RegExp(pattern, flags);
    } else {
      query[qf] = resQuery[qf];
    }
  }
  if (Object.keys(query).length > 0) {
    return query;
  }
};

module.exports = { toMongo };
<|endoftext|>


<commit_before>/**
 * Internal dependencies.
 */

var Request = require('./request');

/**
 * Runner.
 *
 * @constructor
 */

function Runner() {
  if (!(this instanceof Runner)) return new Runner();
  this.request = new Request;
}

/**
 * Set the HTTP method to "GET" and also set `url`.
 *
 * @param {String} url
 * @param {Function} `fn` if provided will perform the test instantly
 * @returns {Runner} self
 * @api public
 */

Runner.prototype.get = function(url, fn) {
  this.request.method('GET');
  this.request.url(url);
  if (fn) this.end(fn);
  return this;
};

/**
 * Perform the test.
 *
 * @param {Function} fn
 * @api public
 */

Runner.prototype.end = function(fn) {
  this.request.exec(fn);
};

/**
 * Primary export.
 */

module.exports = Runner;
<commit_msg>Introduce #method and #url<commit_after>/**
 * External dependencies.
 */

var http = require('http');

/**
 * Internal dependencies.
 */

var Request = require('./request');

/**
 * Runner.
 *
 * @constructor
 */

function Runner() {
  if (!(this instanceof Runner)) return new Runner();
  this.request = new Request;
}

/**
 * Set HTTP method.
 *
 * @param {String} http method
 * @returns {Runner} self
 * @api public
 */

Runner.prototype.method = function(method) {
  this.request.method(method);
  return this;
};

/**
 * Set URL.
 *
 * @param {String} url
 * @returns {Runner} self
 * @api public
 */

Runner.prototype.url = function(url) {
  this.request.url(url);
  return this;
};

/**
 * Perform the test.
 *
 * @param {Function} fn
 * @api public
 */

Runner.prototype.end = function(fn) {
  this.request.exec(fn);
};

/**
 * Sugar syntax for `method`, `url` and `end`.
 *
 * @param {String} url
 * @param {Function} `fn` if provided will perform the test instantly
 * @returns {Runner} self
 * @api public
 */

['GET'].forEach(function(method) {
  Runner.prototype[method.toLowerCase()] = function(url, fn) {
    this.method(method);
    this.url(url);
    if (fn) this.end(fn);
    return this;
  };
});

/**
 * Primary export.
 */

module.exports = Runner;
<|endoftext|>


<commit_before>/** @babel */
/** @jsx etch.dom */

import path from 'path'
import electron from 'electron'
import {CompositeDisposable, TextEditor} from 'atom'
import etch from 'etch'

import PackageCard from './package-card'
import ErrorView from './error-view'

const PackageNameRegex = /config\/install\/(package|theme):([a-z0-9-_]+)/i
import hostedGitInfo from 'hosted-git-info'

export default class InstallPanel {
  constructor (settingsView, packageManager) {
    this.settingsView = settingsView
    this.packageManager = packageManager
    this.disposables = new CompositeDisposable()
    this.client = this.packageManager.getClient()
    this.atomIoURL = 'https://atom.io/packages'

    etch.initialize(this)

    this.refs.searchMessage.style.display = 'none'

    this.refs.searchEditor.setPlaceholderText('Search packages')
    this.searchType = 'packages'
    this.disposables.add(
      this.packageManager.on('package-install-failed', ({pack, error}) => {
        this.refs.searchErrors.appendChild(new ErrorView(this.packageManager, error).element)
      })
    )
    this.disposables.add(
      this.packageManager.on('package-installed theme-installed', ({pack}) => {
        const gitUrlInfo =
          (this.currentGitPackageCard && this.currentGitPackageCard.pack && this.currentGitPackageCard.pack.gitUrlInfo) ?
          this.currentGitPackageCard.pack.gitUrlInfo :
          null

        if (gitUrlInfo && gitUrlInfo === pack.gitUrlInfo) {
          this.updateGitPackageCard(pack)
        }
      })
    )

    this.disposables.add(atom.commands.add(this.refs.searchEditor.element, 'core:confirm', () => {
      this.performSearch()
    }))
    this.disposables.add(atom.commands.add(this.element, {
      'core:move-up': () => { this.scrollUp() },
      'core:move-down': () => { this.scrollDown() },
      'core:page-up': () => { this.pageUp() },
      'core:page-down': () => { this.pageDown() },
      'core:move-to-top': () => { this.scrollToTop() },
      'core:move-to-bottom': () => { this.scrollToBottom() }
    }))

    this.loadFeaturedPackages()
  }

  destroy () {
    this.disposables.dispose()
    return etch.destroy(this)
  }

  update () {}

  focus () {
    this.refs.searchEditor.element.focus()
  }

  show () {
    this.element.style.display = ''
  }

  render () {
    return (

Install Packages

              Packages are published to
              atom.io
               and are installed to {path.join(process.env.ATOM_HOME, 'packages')}

                <TextEditor mini ref='searchEditor' />

                <button ref='searchPackagesButton' className='btn btn-default selected' onclick={this.didClickSearchPackagesButton.bind(this)}>Packages</button>
                <button ref='searchThemesButton' className='btn btn-default' onclick={this.didClickSearchThemesButton.bind(this)}>Themes</button>

    )
  }

  setSearchType (searchType) {
    if (searchType === 'theme') {
      this.searchType = 'themes'
      this.refs.searchThemesButton.classList.add('selected')
      this.refs.searchPackagesButton.classList.remove('selected')
      this.refs.searchEditor.setPlaceholderText('Search themes')
      this.refs.publishedToText.textContent = 'Themes are published to '
      this.atomIoURL = 'https://atom.io/themes'
      this.loadFeaturedPackages(true)
    } else if (searchType === 'package') {
      this.searchType = 'packages'
      this.refs.searchPackagesButton.classList.add('selected')
      this.refs.searchThemesButton.classList.remove('selected')
      this.refs.searchEditor.setPlaceholderText('Search packages')
      this.refs.publishedToText.textContent = 'Packages are published to '
      this.atomIoURL = 'https://atom.io/packages'
      this.loadFeaturedPackages()
    }
  }

  beforeShow (options) {
    if (options && options.uri) {
      const query = this.extractQueryFromURI(options.uri)
      if (query != null) {
        const {searchType, packageName} = query
        this.setSearchType(searchType)
        this.refs.searchEditor.setText(packageName)
        this.performSearch()
      }
    }
  }

  extractQueryFromURI (uri) {
    const matches = PackageNameRegex.exec(uri)
    if (matches) {
      const [, searchType, packageName] = Array.from(matches)
      return {searchType, packageName}
    } else {
      return null
    }
  }

  performSearch () {
    const query = this.refs.searchEditor.getText().trim().toLowerCase()
    if (query) {
      this.performSearchForQuery(query)
    }
  }

  performSearchForQuery (query) {
    let gitUrlInfo
    if (gitUrlInfo = hostedGitInfo.fromUrl(query)) {
      const type = gitUrlInfo.default
      if (type === 'sshurl' || type === 'https' || type === 'shortcut') {
        this.showGitInstallPackageCard({name: query, gitUrlInfo})
      }
    } else {
      this.search(query)
    }
  }

  showGitInstallPackageCard (pack) {
    if (this.currentGitPackageCard) {
      this.currentGitPackageCard.destroy()
    }

    this.currentGitPackageCard = this.getPackageCardView(pack)
    this.currentGitPackageCard.displayGitPackageInstallInformation()
    this.replaceCurrentGitPackageCardView()
  }

  updateGitPackageCard (pack) {
    if (this.currentGitPackageCard) {
      this.currentGitPackageCard.destroy()
    }

    this.currentGitPackageCard = this.getPackageCardView(pack)
    this.replaceCurrentGitPackageCardView()
  }

  replaceCurrentGitPackageCardView () {
    this.refs.resultsContainer.innerHTML = ''
    this.addPackageCardView(this.refs.resultsContainer, this.currentGitPackageCard)
  }

  async search (query) {
    this.refs.resultsContainer.innerHTML = ''
    this.refs.searchMessage.textContent = `Searching ${this.searchType} for \u201C${query}\u201D\u2026`
    this.refs.searchMessage.style.display = ''

    const options = {}
    options[this.searchType] = true

    try {
      const packages = (await this.client.search(query, options)) || []
      this.refs.resultsContainer.innerHTML = ''
      this.refs.searchMessage.style.display = 'none'
      if (packages.length === 0) {
        this.showNoResultMessage(query)
      }
      this.highlightExactMatch(this.refs.resultsContainer, query, packages)
      this.addCloseMatches(this.refs.resultsContainer, query, packages)
      this.addPackageViews(this.refs.resultsContainer, packages)
    } catch (error) {
      this.refs.searchMessage.style.display = 'none'
      this.refs.searchErrors.appendChild(new ErrorView(this.packageManager, error).element)
    }
  }

  showNoResultMessage (query) {
    this.refs.searchMessage.textContent = `No ${this.searchType.replace(/s$/, '')} results for \u201C${query}\u201D`
    this.refs.searchMessage.style.display = ''
  }

  highlightExactMatch (container, query, packages) {
    const exactMatch = packages.filter(pkg => pkg.name.toLowerCase() === query)[0]

    if (exactMatch) {
      this.addPackageCardView(container, this.getPackageCardView(exactMatch))
      packages.splice(packages.indexOf(exactMatch), 1)
    }
  }

  addCloseMatches (container, query, packages) {
    const matches = packages.filter(pkg => pkg.name.toLowerCase().indexOf(query) >= 0)

    for (let pack of matches) {
      this.addPackageCardView(container, this.getPackageCardView(pack))
      packages.splice(packages.indexOf(pack), 1)
    }
  }

  addPackageViews (container, packages) {
    for (const pack of packages) {
      this.addPackageCardView(container, this.getPackageCardView(pack))
    }
  }

  addPackageCardView (container, packageCard) {
    const packageRow = document.createElement('div')
    packageRow.classList.add('row')
    packageRow.appendChild(packageCard.element)
    container.appendChild(packageRow)
  }

  getPackageCardView (pack) {
    return new PackageCard(pack, this.settingsView, this.packageManager, {back: 'Install'})
  }

  filterPackages (packages, themes) {
    return packages.filter(({theme}) => themes ? theme : !theme)
  }

  // Load and display the featured packages that are available to install.
  loadFeaturedPackages (loadThemes) {
    if (loadThemes == null) {
      loadThemes = false
    }
    this.refs.featuredContainer.innerHTML = ''

    if (loadThemes) {
      this.refs.installHeading.textContent = 'Install Themes'
      this.refs.featuredHeading.textContent = 'Featured Themes'
      this.refs.loadingMessage.textContent = 'Loading featured themes\u2026'
    } else {
      this.refs.installHeading.textContent = 'Install Packages'
      this.refs.featuredHeading.textContent = 'Featured Packages'
      this.refs.loadingMessage.textContent = 'Loading featured packages\u2026'
    }

    this.refs.loadingMessage.style.display = ''

    const handle = error => {
      this.refs.loadingMessage.style.display = 'none'
      this.refs.featuredErrors.appendChild(new ErrorView(this.packageManager, error).element)
    }

    if (loadThemes) {
      this.client.featuredThemes((error, themes) => {
        if (error) {
          handle(error)
        } else {
          this.refs.loadingMessage.style.display = 'none'
          this.refs.featuredHeading.textContent = 'Featured Themes'
          this.addPackageViews(this.refs.featuredContainer, themes)
        }
      })
    } else {
      this.client.featuredPackages((error, packages) => {
        if (error) {
          handle(error)
        } else {
          this.refs.loadingMessage.style.display = 'none'
          this.refs.featuredHeading.textContent = 'Featured Packages'
          this.addPackageViews(this.refs.featuredContainer, packages)
        }
      })
    }
  }

  didClickOpenAtomIo (event) {
    event.preventDefault()
    electron.shell.openExternal(this.atomIoURL)
  }

  didClickSearchPackagesButton () {
    if (!this.refs.searchPackagesButton.classList.contains('selected')) {
      this.setSearchType('package')
    }

    this.performSearch()
  }

  didClickSearchThemesButton () {
    if (!this.refs.searchThemesButton.classList.contains('selected')) {
      this.setSearchType('theme')
    }

    this.performSearch()
  }

  scrollUp () {
    this.element.scrollTop -= document.body.offsetHeight / 20
  }

  scrollDown () {
    this.element.scrollTop += document.body.offsetHeight / 20
  }

  pageUp () {
    this.element.scrollTop -= this.element.offsetHeight
  }

  pageDown () {
    this.element.scrollTop += this.element.offsetHeight
  }

  scrollToTop () {
    this.element.scrollTop = 0
  }

  scrollToBottom () {
    this.element.scrollTop = this.element.scrollHeight
  }
}
<commit_msg>Improve search in "Install Panel" to be performed automatically<commit_after>/** @babel */
/** @jsx etch.dom */

import path from 'path'
import electron from 'electron'
import {CompositeDisposable, TextEditor} from 'atom'
import etch from 'etch'

import PackageCard from './package-card'
import ErrorView from './error-view'

const PackageNameRegex = /config\/install\/(package|theme):([a-z0-9-_]+)/i
import hostedGitInfo from 'hosted-git-info'

export default class InstallPanel {
  constructor (settingsView, packageManager) {
    this.settingsView = settingsView
    this.packageManager = packageManager
    this.disposables = new CompositeDisposable()
    this.client = this.packageManager.getClient()
    this.atomIoURL = 'https://atom.io/packages'

    etch.initialize(this)

    this.refs.searchMessage.style.display = 'none'

    this.refs.searchEditor.setPlaceholderText('Search packages')
    this.searchType = 'packages'
    this.disposables.add(
      this.packageManager.on('package-install-failed', ({pack, error}) => {
        this.refs.searchErrors.appendChild(new ErrorView(this.packageManager, error).element)
      })
    )
    this.disposables.add(
      this.packageManager.on('package-installed theme-installed', ({pack}) => {
        const gitUrlInfo =
          (this.currentGitPackageCard && this.currentGitPackageCard.pack && this.currentGitPackageCard.pack.gitUrlInfo) ?
          this.currentGitPackageCard.pack.gitUrlInfo :
          null

        if (gitUrlInfo && gitUrlInfo === pack.gitUrlInfo) {
          this.updateGitPackageCard(pack)
        }
      })
    )
    this.disposables.add(
      this.refs.searchEditor.onDidStopChanging(() => {
        this.performSearch()
      })
    )
    this.disposables.add(atom.commands.add(this.element, {
      'core:move-up': () => { this.scrollUp() },
      'core:move-down': () => { this.scrollDown() },
      'core:page-up': () => { this.pageUp() },
      'core:page-down': () => { this.pageDown() },
      'core:move-to-top': () => { this.scrollToTop() },
      'core:move-to-bottom': () => { this.scrollToBottom() }
    }))

    this.loadFeaturedPackages()
  }

  destroy () {
    this.disposables.dispose()
    return etch.destroy(this)
  }

  update () {}

  focus () {
    this.refs.searchEditor.element.focus()
  }

  show () {
    this.element.style.display = ''
  }

  render () {
    return (

Install Packages

              Packages are published to
              atom.io
               and are installed to {path.join(process.env.ATOM_HOME, 'packages')}

                <TextEditor mini ref='searchEditor' />

                <button ref='searchPackagesButton' className='btn btn-default selected' onclick={this.didClickSearchPackagesButton.bind(this)}>Packages</button>
                <button ref='searchThemesButton' className='btn btn-default' onclick={this.didClickSearchThemesButton.bind(this)}>Themes</button>

    )
  }

  setSearchType (searchType) {
    if (searchType === 'theme') {
      this.searchType = 'themes'
      this.refs.searchThemesButton.classList.add('selected')
      this.refs.searchPackagesButton.classList.remove('selected')
      this.refs.searchEditor.setPlaceholderText('Search themes')
      this.refs.publishedToText.textContent = 'Themes are published to '
      this.atomIoURL = 'https://atom.io/themes'
      this.loadFeaturedPackages(true)
    } else if (searchType === 'package') {
      this.searchType = 'packages'
      this.refs.searchPackagesButton.classList.add('selected')
      this.refs.searchThemesButton.classList.remove('selected')
      this.refs.searchEditor.setPlaceholderText('Search packages')
      this.refs.publishedToText.textContent = 'Packages are published to '
      this.atomIoURL = 'https://atom.io/packages'
      this.loadFeaturedPackages()
    }
  }

  beforeShow (options) {
    if (options && options.uri) {
      const query = this.extractQueryFromURI(options.uri)
      if (query != null) {
        const {searchType, packageName} = query
        this.setSearchType(searchType)
        this.refs.searchEditor.setText(packageName)
        this.performSearch()
      }
    }
  }

  extractQueryFromURI (uri) {
    const matches = PackageNameRegex.exec(uri)
    if (matches) {
      const [, searchType, packageName] = Array.from(matches)
      return {searchType, packageName}
    } else {
      return null
    }
  }

  performSearch () {
    const query = this.refs.searchEditor.getText().trim().toLowerCase()
    if (query) {
      this.performSearchForQuery(query)
    }
  }

  performSearchForQuery (query) {
    let gitUrlInfo
    if (gitUrlInfo = hostedGitInfo.fromUrl(query)) {
      const type = gitUrlInfo.default
      if (type === 'sshurl' || type === 'https' || type === 'shortcut') {
        this.showGitInstallPackageCard({name: query, gitUrlInfo})
      }
    } else {
      this.search(query)
    }
  }

  showGitInstallPackageCard (pack) {
    if (this.currentGitPackageCard) {
      this.currentGitPackageCard.destroy()
    }

    this.currentGitPackageCard = this.getPackageCardView(pack)
    this.currentGitPackageCard.displayGitPackageInstallInformation()
    this.replaceCurrentGitPackageCardView()
  }

  updateGitPackageCard (pack) {
    if (this.currentGitPackageCard) {
      this.currentGitPackageCard.destroy()
    }

    this.currentGitPackageCard = this.getPackageCardView(pack)
    this.replaceCurrentGitPackageCardView()
  }

  replaceCurrentGitPackageCardView () {
    this.refs.resultsContainer.innerHTML = ''
    this.addPackageCardView(this.refs.resultsContainer, this.currentGitPackageCard)
  }

  async search (query) {
    this.refs.resultsContainer.innerHTML = ''
    this.refs.searchMessage.textContent = `Searching ${this.searchType} for \u201C${query}\u201D\u2026`
    this.refs.searchMessage.style.display = ''

    const options = {}
    options[this.searchType] = true

    try {
      const packages = (await this.client.search(query, options)) || []
      this.refs.resultsContainer.innerHTML = ''
      this.refs.searchMessage.style.display = 'none'
      if (packages.length === 0) {
        this.showNoResultMessage(query)
      }
      this.highlightExactMatch(this.refs.resultsContainer, query, packages)
      this.addCloseMatches(this.refs.resultsContainer, query, packages)
      this.addPackageViews(this.refs.resultsContainer, packages)
    } catch (error) {
      this.refs.searchMessage.style.display = 'none'
      this.refs.searchErrors.appendChild(new ErrorView(this.packageManager, error).element)
    }
  }

  showNoResultMessage (query) {
    this.refs.searchMessage.textContent = `No ${this.searchType.replace(/s$/, '')} results for \u201C${query}\u201D`
    this.refs.searchMessage.style.display = ''
  }

  highlightExactMatch (container, query, packages) {
    const exactMatch = packages.filter(pkg => pkg.name.toLowerCase() === query)[0]

    if (exactMatch) {
      this.addPackageCardView(container, this.getPackageCardView(exactMatch))
      packages.splice(packages.indexOf(exactMatch), 1)
    }
  }

  addCloseMatches (container, query, packages) {
    const matches = packages.filter(pkg => pkg.name.toLowerCase().indexOf(query) >= 0)

    for (let pack of matches) {
      this.addPackageCardView(container, this.getPackageCardView(pack))
      packages.splice(packages.indexOf(pack), 1)
    }
  }

  addPackageViews (container, packages) {
    for (const pack of packages) {
      this.addPackageCardView(container, this.getPackageCardView(pack))
    }
  }

  addPackageCardView (container, packageCard) {
    const packageRow = document.createElement('div')
    packageRow.classList.add('row')
    packageRow.appendChild(packageCard.element)
    container.appendChild(packageRow)
  }

  getPackageCardView (pack) {
    return new PackageCard(pack, this.settingsView, this.packageManager, {back: 'Install'})
  }

  filterPackages (packages, themes) {
    return packages.filter(({theme}) => themes ? theme : !theme)
  }

  // Load and display the featured packages that are available to install.
  loadFeaturedPackages (loadThemes) {
    if (loadThemes == null) {
      loadThemes = false
    }
    this.refs.featuredContainer.innerHTML = ''

    if (loadThemes) {
      this.refs.installHeading.textContent = 'Install Themes'
      this.refs.featuredHeading.textContent = 'Featured Themes'
      this.refs.loadingMessage.textContent = 'Loading featured themes\u2026'
    } else {
      this.refs.installHeading.textContent = 'Install Packages'
      this.refs.featuredHeading.textContent = 'Featured Packages'
      this.refs.loadingMessage.textContent = 'Loading featured packages\u2026'
    }

    this.refs.loadingMessage.style.display = ''

    const handle = error => {
      this.refs.loadingMessage.style.display = 'none'
      this.refs.featuredErrors.appendChild(new ErrorView(this.packageManager, error).element)
    }

    if (loadThemes) {
      this.client.featuredThemes((error, themes) => {
        if (error) {
          handle(error)
        } else {
          this.refs.loadingMessage.style.display = 'none'
          this.refs.featuredHeading.textContent = 'Featured Themes'
          this.addPackageViews(this.refs.featuredContainer, themes)
        }
      })
    } else {
      this.client.featuredPackages((error, packages) => {
        if (error) {
          handle(error)
        } else {
          this.refs.loadingMessage.style.display = 'none'
          this.refs.featuredHeading.textContent = 'Featured Packages'
          this.addPackageViews(this.refs.featuredContainer, packages)
        }
      })
    }
  }

  didClickOpenAtomIo (event) {
    event.preventDefault()
    electron.shell.openExternal(this.atomIoURL)
  }

  didClickSearchPackagesButton () {
    if (!this.refs.searchPackagesButton.classList.contains('selected')) {
      this.setSearchType('package')
    }

    this.performSearch()
  }

  didClickSearchThemesButton () {
    if (!this.refs.searchThemesButton.classList.contains('selected')) {
      this.setSearchType('theme')
    }

    this.performSearch()
  }

  scrollUp () {
    this.element.scrollTop -= document.body.offsetHeight / 20
  }

  scrollDown () {
    this.element.scrollTop += document.body.offsetHeight / 20
  }

  pageUp () {
    this.element.scrollTop -= this.element.offsetHeight
  }

  pageDown () {
    this.element.scrollTop += this.element.offsetHeight
  }

  scrollToTop () {
    this.element.scrollTop = 0
  }

  scrollToBottom () {
    this.element.scrollTop = this.element.scrollHeight
  }
}
<|endoftext|>


<commit_before>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var fs = require('fs');
var sys = require('sys');
var path = require('path');
var events = require('events');

var Errorf = require('util/misc').Errof;

/**
 * A resource that is backed by a directory.
 * @param {String} parentDir The path to this resource's parent directory.
 * @param {String} name The name of this resource's directory.
 * @constructor
 */
function DirectoryResource(parentDir, name) {
  this.name = name;
  this.root = path.join(parentDir, name);
  this._jobs = [];
  this._running = null;
}

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

/**
 * If no job is currently being processed, start the one at the head of the
 * queue.
 */
DirectoryResource.prototype._cycle = function() {
  if (!this._running && this._jobs.length > 0) {
    this._running = this._jobs.shift();
    this._running.start();
  }
};

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

/**
 * Execute the given job against the resource identified by the specified name.
 * @param {String} name The name of the resource to execute the job against.
 * @param {Object} job The job to execute.
 */
ResourceManager.prototype.runJob = function(name, job) {
  if (!this.resources[name]) {
    this.resources[name] = new this.resourceType(name);
  }

  job.enqueueFor(this.resources[name]);
};

exports.ResourceManager = ResourceManager;
exports.DirectoryResource = DirectoryResource;
<commit_msg>use a getter for resource roots to keep constructor cleaner<commit_after>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var fs = require('fs');
var sys = require('sys');
var path = require('path');
var events = require('events');

var Errorf = require('util/misc').Errof;

/**
 * A resource that is backed by a directory.
 * @param {String} name The name of this resource's directory.
 * @constructor
 */
function DirectoryResource(name) {
  this.name = name;
  this.parentDir = undefined;
  this._jobs = [];
  this._running = null;
}

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

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

/**
 * If no job is currently being processed, start the one at the head of the
 * queue.
 */
DirectoryResource.prototype._cycle = function() {
  if (!this._running && this._jobs.length > 0) {
    this._running = this._jobs.shift();
    this._running.start();
  }
};

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

/**
 * Execute the given job against the resource identified by the specified name.
 * @param {String} name The name of the resource to execute the job against.
 * @param {Object} job The job to execute.
 */
ResourceManager.prototype.runJob = function(name, job) {
  if (!this.resources[name]) {
    this.resources[name] = new this.resourceType(name);
  }

  job.enqueueFor(this.resources[name]);
};

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