<commit_before>var StatementWS = (function(){
    "use strict";

    //Batch refer to number of data get from ws service per request
    //chunk refer to number of data populate to ui for each append
    //receive means receive from ws service
    //consume means consume by UI and displayed to page

    var batchSize,
        chunkSize,
        noMoreData,
        pending,
        currentBatch,
        transactionsReceived,
        transactionsConsumed;

    var tableExist = function(){
        return document.getElementById("statement-table");
    };

    var finishedConsumed = function(){
        return transactionsConsumed === transactionsReceived;
    };

    function statementHandler(response){
        if(response.hasOwnProperty('error')) {
            StatementUI.errorMessage(response.error.message);
            return;
        }

        pending = false;

        var statement = response.statement;
        currentBatch = statement.transactions;
        transactionsReceived += currentBatch.length;

        if (currentBatch.length < batchSize){
            noMoreData = true;
        }

        if (!tableExist()) {
            StatementUI.createEmptyStatementTable().appendTo("#statement-ws-container");
            StatementUI.updateStatementTable(getNextChunkStatement());

            // Show a message when the table is empty
            if ((transactionsReceived === 0) && (currentBatch.length === 0)) {
                $('#statement-table tbody')
                    .append($('<tr/>', {class: "flex-tr"})
                        .append($('<td/>', {colspan: 7})
                            .append($('<p/>', {class: "notice-msg center-text", text: page.text.localize("Your account has no trading activity.")})
                            )
                        )
                    );
            } else if(page.language().toLowerCase() === 'ja') {
                $('#download_csv').removeClass('invisible').find('a').click(function(){StatementUI.exportCSV();});
            }
        }
        showLocalTimeOnHover('td.date');
    }

    function getNextBatchStatement(){
        StatementData.getStatement({offset: transactionsReceived, limit: batchSize});
        pending = true;
    }

    function getNextChunkStatement(){
        var chunk = currentBatch.splice(0, chunkSize);
        transactionsConsumed += chunk.length;
        $('#rows_count').text(transactionsConsumed);
        return chunk;
    }

    function loadStatementChunkWhenScroll(){
        $(document).scroll(function(){

            function hidableHeight(percentage){
                var totalHidable = $(document).height() - $(window).height();
                return Math.floor(totalHidable * percentage / 100);
            }

            var pFromTop = $(document).scrollTop();

            if (!tableExist()){
                return;
            }

            if (pFromTop < hidableHeight(70)) {
                return;
            }

            if (finishedConsumed() && !noMoreData && !pending) {
                getNextBatchStatement();
                return;
            }

            if (!finishedConsumed()){
                StatementUI.updateStatementTable(getNextChunkStatement());
            }
        });
    }

    function initTable(){
        pending = false;
        noMoreData = false;

        currentBatch = [];

        transactionsReceived = 0;
        transactionsConsumed = 0;

        StatementUI.errorMessage(null);

        StatementUI.clearTableContent();
    }

    function initPage(){
        batchSize = 200;
        chunkSize = batchSize/2;
        noMoreData = false;
        pending = false;            //serve as a lock to prevent ws request is sequential
        currentBatch = [];
        transactionsReceived = 0;
        transactionsConsumed = 0;
        StatementData.initSocket();
        Content.populate();
        getNextBatchStatement();
        loadStatementChunkWhenScroll();
    }

    function cleanStatementPageState(){
        initTable();
    }

    return {
        init: initPage,
        statementHandler: statementHandler,
        clean: cleanStatementPageState
    };
}());

module.exports = {
    StatementWS: StatementWS,
};
<commit_msg>add nowrap to action column header of statement<commit_after>var StatementWS = (function(){
    "use strict";

    //Batch refer to number of data get from ws service per request
    //chunk refer to number of data populate to ui for each append
    //receive means receive from ws service
    //consume means consume by UI and displayed to page

    var batchSize,
        chunkSize,
        noMoreData,
        pending,
        currentBatch,
        transactionsReceived,
        transactionsConsumed;

    var tableExist = function(){
        return document.getElementById("statement-table");
    };

    var finishedConsumed = function(){
        return transactionsConsumed === transactionsReceived;
    };

    function statementHandler(response){
        if(response.hasOwnProperty('error')) {
            StatementUI.errorMessage(response.error.message);
            return;
        }

        pending = false;

        var statement = response.statement;
        currentBatch = statement.transactions;
        transactionsReceived += currentBatch.length;

        if (currentBatch.length < batchSize){
            noMoreData = true;
        }

        if (!tableExist()) {
            StatementUI.createEmptyStatementTable().appendTo("#statement-ws-container");
            $('.act').addClass('nowrap');
            StatementUI.updateStatementTable(getNextChunkStatement());

            // Show a message when the table is empty
            if ((transactionsReceived === 0) && (currentBatch.length === 0)) {
                $('#statement-table tbody')
                    .append($('<tr/>', {class: "flex-tr"})
                        .append($('<td/>', {colspan: 7})
                            .append($('<p/>', {class: "notice-msg center-text", text: page.text.localize("Your account has no trading activity.")})
                            )
                        )
                    );
            } else if(page.language().toLowerCase() === 'ja') {
                $('#download_csv').removeClass('invisible').find('a').click(function(){StatementUI.exportCSV();});
            }
        }
        showLocalTimeOnHover('td.date');
    }

    function getNextBatchStatement(){
        StatementData.getStatement({offset: transactionsReceived, limit: batchSize});
        pending = true;
    }

    function getNextChunkStatement(){
        var chunk = currentBatch.splice(0, chunkSize);
        transactionsConsumed += chunk.length;
        $('#rows_count').text(transactionsConsumed);
        return chunk;
    }

    function loadStatementChunkWhenScroll(){
        $(document).scroll(function(){

            function hidableHeight(percentage){
                var totalHidable = $(document).height() - $(window).height();
                return Math.floor(totalHidable * percentage / 100);
            }

            var pFromTop = $(document).scrollTop();

            if (!tableExist()){
                return;
            }

            if (pFromTop < hidableHeight(70)) {
                return;
            }

            if (finishedConsumed() && !noMoreData && !pending) {
                getNextBatchStatement();
                return;
            }

            if (!finishedConsumed()){
                StatementUI.updateStatementTable(getNextChunkStatement());
            }
        });
    }

    function initTable(){
        pending = false;
        noMoreData = false;

        currentBatch = [];

        transactionsReceived = 0;
        transactionsConsumed = 0;

        StatementUI.errorMessage(null);

        StatementUI.clearTableContent();
    }

    function initPage(){
        batchSize = 200;
        chunkSize = batchSize/2;
        noMoreData = false;
        pending = false;            //serve as a lock to prevent ws request is sequential
        currentBatch = [];
        transactionsReceived = 0;
        transactionsConsumed = 0;
        StatementData.initSocket();
        Content.populate();
        getNextBatchStatement();
        loadStatementChunkWhenScroll();
    }

    function cleanStatementPageState(){
        initTable();
    }

    return {
        init: initPage,
        statementHandler: statementHandler,
        clean: cleanStatementPageState
    };
}());

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


<commit_before>define(['nu/stream', 'ecma_unparse/beautify'],
function(stream, beautify){

/**
 * Transforms a stream of tokens to strings.
 */
var print = (function(){
    var mapper = function(x) {
        if (!x)
            return '';

        switch (x.type) {
        case 'Null':
            return 'null';
        case 'Comment':
            return "/*" + x.value + "*/";
        case 'String':
            var value = x.value
                .replace('\\', '\\\\')
                .replace('\n', '\\n')
                .replace('\'', '\\\'');
            return (x.value.indexOf('"') !== -1 ?
                "'" + value + "'" :
                '"' + value + '"');
        default:
            return x.value;
        }
    };

    return function(s) {
        return beautify(
            stream.foldl(
                function(p, c) { return p + c; },
                '',
                stream.map(mapper, s)));
    };
}());

return {
    'print': print
};

});<commit_msg>More string escape fixes<commit_after>define(['nu/stream', 'ecma_unparse/beautify'],
function(stream, beautify){

/**
 * Transforms a stream of tokens to strings.
 */
var print = (function(){
    var mapper = function(x) {
        if (!x)
            return '';

        switch (x.type) {
        case 'Null':
            return 'null';
        case 'Comment':
            return "/*" + x.value + "*/";
        case 'String':
            var value = x.value
                .replace('\\', '\\\\')
                .replace('\n', '\\n')
                .replace('\'', '\\\'')
                .replace('\u0008', '\\u0008')
                .replace('\u000C', '\\u000C')
                .replace('\u000A', '\\u000A')
                .replace('\u000D', '\\u000D')
                .replace('\u0009', '\\u0009')
                .replace('\u000B', '\\u000B');
            return (x.value.indexOf('"') !== -1 ?
                "'" + value + "'" :
                '"' + value + '"');
        default:
            return x.value;
        }
    };

    return function(s) {
        return beautify(
            stream.foldl(
                function(p, c) { return p + c; },
                '',
                stream.map(mapper, s)));
    };
}());

return {
    'print': print
};

});<|endoftext|>


<commit_before>let IO = require("io.js");

Object.defineProperty(
  this, "logfile",
  {
    get: function() {
      delete this.logfile;
      return this.logfile = IO.open_truncate("result.log");
    },
    configurable: true
  }
);

function print_error(msg, loc) {
  let text = "MARV ERROR: ";
  if (loc) {
    text += loc + " ";
  }
  text += msg;
  printErr(text);
  logfile.write(text+"\n");
}

function print_warning(msg, loc) {
  let text = "MARV WARNING: ";
  if (loc) {
    text += loc + " ";
  }
  text += msg;
  printErr(text);
  logfile.write(text+"\n");
}

function print_progress(msg) {
  print(msg);
  logfile.write("MARV INFO: "+msg+"\n");
}

exports.error = print_error;
exports.warning = print_warning;
exports.progress = print_progress;<commit_msg>[Cleanup] log.js: |logfile| is now a regular variable<commit_after>let IO = require("io.js");
let logfile = IO.open_truncate("result.log");

function print_error(msg, loc) {
  let text = "MARV ERROR: ";
  if (loc) {
    text += loc + " ";
  }
  text += msg;
  printErr(text);
  logfile.write(text+"\n");
}

function print_warning(msg, loc) {
  let text = "MARV WARNING: ";
  if (loc) {
    text += loc + " ";
  }
  text += msg;
  printErr(text);
  logfile.write(text+"\n");
}

function print_progress(msg) {
  print(msg);
  logfile.write("MARV INFO: "+msg+"\n");
}

exports.error = print_error;
exports.warning = print_warning;
exports.progress = print_progress;<|endoftext|>


<commit_before>'use babel';

import PyOcdView from './pyocd-view';
import { CompositeDisposable } from 'atom';

import fs from 'fs';
import path from 'path';
import findit from 'findit';

import PyOcdSettings from './pyocd-settings';
import PyOcdProgrammer from './pyocd-programmer';
import getCurrentProjectDir from './pyocd-util';

export default {

  pyOcdSettings: null,
  pyOcdView: null,
  pyOcdModalPanel: null,
  subscriptions: null,
  busyRegistry: null,

  config: {
    pyocdFlashtoolBinary: {
      title: 'pyocd-flashtool binary',
      description: 'binary (with our without path) for the `pyocd-flashtool` command',
      type: 'string',
      default: 'pyocd-flashtool',
      order: 10
    },
  },

  activate(state) {

    require('atom-package-deps').install('pyocd', true)
        .then(function() {
          console.log('dependencies installed for pyocd');
    });

    this.pyOcdSettings = new PyOcdSettings();
    this.pyOcdView = new PyOcdView(state.pyOcdViewState);

    this.pyOcdSettings.read();
    this.pyOcdView.fromSettings(this.pyOcdSettings);

    this.pyOcdView.onFlash(
        () => this.flash()
    );

    this.pyOcdView.onErase(
        () => this.erase()
    );

    this.pyOcdView.onClose(
        () => this.toggle()
    );

    this.pyOcdModalPanel = atom.workspace.addModalPanel({
      item: this.pyOcdView.getElement(),
      visible: false
    });

    this.subscriptions = new CompositeDisposable();

    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'pyocd:toggle': () => this.toggle()
    }));

    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'pyocd:flash': () => this.flash()
    }));

    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'pyocd:erase': () => this.erase()
    }));

    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'pyocd:test': () => this.guessBinaryOutput((files) => {
          atom.notifications.addInfo('Found binary files', {detail: files})
        })
      }));
  },

  consumeBusy(registry) {
    this.busyRegistry = registry;
  },

  deactivate() {
    this.pyOcdModalPanel.destroy();
    this.subscriptions.dispose();
    this.pyOcdView.destroy();
  },

  serialize() {
    return {
      pyOcdViewState: this.pyOcdView.serialize()
    };
  },

  guessBinaryOutput(callback) {

    var prjDir = getCurrentProjectDir();
    var finder = findit(prjDir);
    var binaryFiles = [];

    finder.on('file', function (file, stat) {
        if(file.endsWith('.hex') || file.endsWith('.bin')) {
          console.log('found binary: ' + file);
          callback(file.substr(prjDir.length + 1));
          this.stop();
        }
    });

    finder.on('end', function () {
      if(callback) {
        callback(null);
      }
    });
  },

  notifySuccess(message) {

    var parts = message.split('\n');
    var header = 'pyOCD success!';
    var body = null;

    if(parts.length) {
      if(parts[0]) {
        header = parts[0];
      }
      if(parts.length > 1) {
        body = parts.splice(1, parts.length).join('
')
      }
    }

    if(this.pyOcdModalPanel.isVisible()) {
      var m = header;

      if(body) {
          m = m + '

' + body + '
'
      }
      this.pyOcdView.setOutput(m);
      this.pyOcdView.showOutput(true);
    }
    else {
      if(body) {
        atom.notifications.addSuccess(header, {detail: body});
      }
      else {
        atom.notifications.addSuccess(header);
      }
    }
  },

  notifyError(message) {

    var parts = message.split('\n');
    var header = 'pyOCD failed!';
    var body = null;

    if(parts.length) {
      if(parts[0]) {
        header = parts[0];
      }
      if(parts.length > 1) {
        body = parts.splice(1, parts.length).join('\n')
      }
    }

    if(this.pyOcdModalPanel.isVisible()) {
      var m = header;

      if(body) {
        m = m + '

' + body + '

'
      }

      this.pyOcdView.setError(m);
      this.pyOcdView.showError(true);
    }
    else {
      if(body) {
        atom.notifications.addError(header, {detail: body});
      }
      else {
        atom.notifications.addError(header);
      }
    }
  },

  showProgress(running) {
    if(this.pyOcdModalPanel.isVisible()) {
      this.pyOcdView.programmerRunning(running);
    }
  },

  flash() {

    var prog = new PyOcdProgrammer(this.busyRegistry);

    if(this.pyOcdModalPanel.isVisible()) {
      this.pyOcdSettings = this.pyOcdView.toSettings();
    }
    else {
      this.pyOcdSettings.read();
    }

    this.pyOcdSettings.dump();

    if(this.pyOcdSettings.file.length == 0) {
        this.notifyError('No file to flash was given!');
        return;
    }

    this.showProgress(true);

    prog.flash(
      this.pyOcdSettings,
      (message) => {
        this.showProgress(false);
        this.notifySuccess(message);
      },
      (message) => {
        this.showProgress(false);
        this.notifyError(message);
      }
    );
  },

  erase() {

    var prog = new PyOcdProgrammer(this.busyRegistry);

    if(this.pyOcdModalPanel.isVisible()) {
      this.pyOcdSettings = this.pyOcdView.toSettings();
    }
    else {
      this.pyOcdSettings.read();
    }

    this.pyOcdSettings.dump();

    this.pyOcdView.programmerRunning(true);

    prog.erase(
      this.pyOcdSettings,
      (message) => {
        this.showProgress(false);
        this.notifySuccess(message);
      },
      (message) => {
        this.showProgress(false);
        this.notifyError(message);
      }
    );
  },

  toggle() {

    var prog = new PyOcdProgrammer(this.busyRegistry);

    if(prog.killRunning()) {
      if(this.pyOcdModalPanel.isVisible()) {
          this.pyOcdSettings = this.pyOcdView.toSettings();
          this.pyOcdSettings.write();
          return this.pyOcdModalPanel.hide();
      }
      else {
          this.pyOcdSettings = new PyOcdSettings();
          this.pyOcdSettings.read();

          this.guessBinaryOutput((binfile) => {
            if(this.pyOcdSettings.file == '') {
              console.log('Settig file to: ' + binfile);
              this.pyOcdSettings.file = binfile;
            }

            this.pyOcdView.fromSettings(this.pyOcdSettings);
            this.pyOcdView.hideExtra();

            var prog = new PyOcdProgrammer(this.busyRegistry);

            prog.list(
              this.pyOcdSettings,
              (message) => {
                var bids = [];
                var lines = message.split('\n');

                for(var l in lines) {

                  var parts = lines[l].split(' => ');

                  if(parts.length >= 2) {
                    bids = bids.concat({name: parts[1], id: parts[2]});
                  }
                }
                this.pyOcdView.setBoardIds(this.pyOcdSettings, bids);
              },
              (message) => {
                this.notifyError(message);
              }
            );
          });

          return this.pyOcdModalPanel.show();
      }
    }
  }
};
<commit_msg>removed redundant "<commit_after>'use babel';

import PyOcdView from './pyocd-view';
import { CompositeDisposable } from 'atom';

import fs from 'fs';
import path from 'path';
import findit from 'findit';

import PyOcdSettings from './pyocd-settings';
import PyOcdProgrammer from './pyocd-programmer';
import getCurrentProjectDir from './pyocd-util';

export default {

  pyOcdSettings: null,
  pyOcdView: null,
  pyOcdModalPanel: null,
  subscriptions: null,
  busyRegistry: null,

  config: {
    pyocdFlashtoolBinary: {
      title: 'pyocd-flashtool binary',
      description: 'binary (with our without path) for the `pyocd-flashtool` command',
      type: 'string',
      default: 'pyocd-flashtool',
      order: 10
    },
  },

  activate(state) {

    require('atom-package-deps').install('pyocd', true)
        .then(function() {
          console.log('dependencies installed for pyocd');
    });

    this.pyOcdSettings = new PyOcdSettings();
    this.pyOcdView = new PyOcdView(state.pyOcdViewState);

    this.pyOcdSettings.read();
    this.pyOcdView.fromSettings(this.pyOcdSettings);

    this.pyOcdView.onFlash(
        () => this.flash()
    );

    this.pyOcdView.onErase(
        () => this.erase()
    );

    this.pyOcdView.onClose(
        () => this.toggle()
    );

    this.pyOcdModalPanel = atom.workspace.addModalPanel({
      item: this.pyOcdView.getElement(),
      visible: false
    });

    this.subscriptions = new CompositeDisposable();

    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'pyocd:toggle': () => this.toggle()
    }));

    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'pyocd:flash': () => this.flash()
    }));

    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'pyocd:erase': () => this.erase()
    }));

    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'pyocd:test': () => this.guessBinaryOutput((files) => {
          atom.notifications.addInfo('Found binary files', {detail: files})
        })
      }));
  },

  consumeBusy(registry) {
    this.busyRegistry = registry;
  },

  deactivate() {
    this.pyOcdModalPanel.destroy();
    this.subscriptions.dispose();
    this.pyOcdView.destroy();
  },

  serialize() {
    return {
      pyOcdViewState: this.pyOcdView.serialize()
    };
  },

  guessBinaryOutput(callback) {

    var prjDir = getCurrentProjectDir();
    var finder = findit(prjDir);
    var binaryFiles = [];

    finder.on('file', function (file, stat) {
        if(file.endsWith('.hex') || file.endsWith('.bin')) {
          console.log('found binary: ' + file);
          callback(file.substr(prjDir.length + 1));
          this.stop();
        }
    });

    finder.on('end', function () {
      if(callback) {
        callback(null);
      }
    });
  },

  notifySuccess(message) {

    var parts = message.split('\n');
    var header = 'pyOCD success!';
    var body = null;

    if(parts.length) {
      if(parts[0]) {
        header = parts[0];
      }
      if(parts.length > 1) {
        body = parts.splice(1, parts.length).join('
')
      }
    }

    if(this.pyOcdModalPanel.isVisible()) {
      var m = header;

      if(body) {
          m = m + '

' + body + '
'
      }
      this.pyOcdView.setOutput(m);
      this.pyOcdView.showOutput(true);
    }
    else {
      if(body) {
        atom.notifications.addSuccess(header, {detail: body});
      }
      else {
        atom.notifications.addSuccess(header);
      }
    }
  },

  notifyError(message) {

    var parts = message.split('\n');
    var header = 'pyOCD failed!';
    var body = null;

    if(parts.length) {
      if(parts[0]) {
        header = parts[0];
      }
      if(parts.length > 1) {
        body = parts.splice(1, parts.length).join('\n')
      }
    }

    if(this.pyOcdModalPanel.isVisible()) {
      var m = header;

      if(body) {
        m = m + '

' + body + '

'
      }

      this.pyOcdView.setError(m);
      this.pyOcdView.showError(true);
    }
    else {
      if(body) {
        atom.notifications.addError(header, {detail: body});
      }
      else {
        atom.notifications.addError(header);
      }
    }
  },

  showProgress(running) {
    if(this.pyOcdModalPanel.isVisible()) {
      this.pyOcdView.programmerRunning(running);
    }
  },

  flash() {

    var prog = new PyOcdProgrammer(this.busyRegistry);

    if(this.pyOcdModalPanel.isVisible()) {
      this.pyOcdSettings = this.pyOcdView.toSettings();
    }
    else {
      this.pyOcdSettings.read();
    }

    this.pyOcdSettings.dump();

    if(this.pyOcdSettings.file.length == 0) {
        this.notifyError('No file to flash was given!');
        return;
    }

    this.showProgress(true);

    prog.flash(
      this.pyOcdSettings,
      (message) => {
        this.showProgress(false);
        this.notifySuccess(message);
      },
      (message) => {
        this.showProgress(false);
        this.notifyError(message);
      }
    );
  },

  erase() {

    var prog = new PyOcdProgrammer(this.busyRegistry);

    if(this.pyOcdModalPanel.isVisible()) {
      this.pyOcdSettings = this.pyOcdView.toSettings();
    }
    else {
      this.pyOcdSettings.read();
    }

    this.pyOcdSettings.dump();

    this.pyOcdView.programmerRunning(true);

    prog.erase(
      this.pyOcdSettings,
      (message) => {
        this.showProgress(false);
        this.notifySuccess(message);
      },
      (message) => {
        this.showProgress(false);
        this.notifyError(message);
      }
    );
  },

  toggle() {

    var prog = new PyOcdProgrammer(this.busyRegistry);

    if(prog.killRunning()) {
      if(this.pyOcdModalPanel.isVisible()) {
          this.pyOcdSettings = this.pyOcdView.toSettings();
          this.pyOcdSettings.write();
          return this.pyOcdModalPanel.hide();
      }
      else {
          this.pyOcdSettings = new PyOcdSettings();
          this.pyOcdSettings.read();

          this.guessBinaryOutput((binfile) => {
            if(this.pyOcdSettings.file == '') {
              console.log('Settig file to: ' + binfile);
              this.pyOcdSettings.file = binfile;
            }

            this.pyOcdView.fromSettings(this.pyOcdSettings);
            this.pyOcdView.hideExtra();

            var prog = new PyOcdProgrammer(this.busyRegistry);

            prog.list(
              this.pyOcdSettings,
              (message) => {
                var bids = [];
                var lines = message.split('\n');

                for(var l in lines) {

                  var parts = lines[l].split(' => ');

                  if(parts.length >= 2) {
                    bids = bids.concat({name: parts[1], id: parts[2]});
                  }
                }
                this.pyOcdView.setBoardIds(this.pyOcdSettings, bids);
              },
              (message) => {
                this.notifyError(message);
              }
            );
          });

          return this.pyOcdModalPanel.show();
      }
    }
  }
};
<|endoftext|>


<commit_before>function qsync () {
	var self = this;
	var shim = require("./shim");
	var engine = require("./engine");

	this.middleware = {
		"serial": {
			"default": {},
			"array": {},
			"tasks": {}
		},
		"parallel": {
			"default": {},
			"array": {},
			"tasks": {}
		}
	};

	this.serial = function (array, tasks, callback) {
		var model = clone();

		model.select(model.middleware.serial);
		model.process(arguments);

		return model;
	}

	this.parallel = function (array, tasks, callback) {
		var model = clone();

		model.select(model.middleware.parallel);
		model.process(arguments);

		return model;
	}

	this.queue = function (data, callback) {
		var label, input = self.parameters;
		var label = input.array.length;

		if (input.multiplicity == "single" &&
			input.array.length > 1) {
			input.array = [input.array, data];
		} else {
			input.array.push(data);
		}

		input.multiplicity = "multiple";
		add([data], label.toString(), callback);
		self.nextTick();

		return self;
	}

	this.priority = function (data, priority, callback) {
		var label, input = self.parameters;
		var label = input.array.length;

		if (input.multiplicity == "single" &&
			input.array.length > 1) {
			input.array = [input.array, data];
		} else {
			input.array.push(data);
		}

		input.multiplicity = "multiple";
		add([data], label.toString(), callback);
		self.nextTick();

		return self;
	}

	this.pause = function () {
		if (self.handle) {
			clearImmediate(self.handle);
			self.handle = undefined;
		}

		return self;
	}

	this.resume = function () {
		if (self.handle) {
			clearImmediate(self.handle);
			self.handle = undefined;
		}

		run();
		return self;
	}

	this.nextTick = function () {
		if (!self.handle) {
			self.handle = setImmediate(function () {
				self.handle = undefined;
				run();
			});
		}

		return self;
	}

	this.select = function (middlewares) {
		self.middlewares = middlewares;
	}

	this.process = function (parameters, configure) {
		var input = self.parameters = refine(parameters);

		if (input.tasks instanceof Array) {
			self.type = Array;
		} else if (input.tasks instanceof Function) {
			self.type = Function;
		} else if (input.tasks instanceof Object) {
			self.type = Object;
		}

		self.array = new shim();
		self.tasks = new shim();

		for (var key in input.tasks) {
			self.tasks.add(key.toString());
		}

		self.engine = new engine();
		self.engine.init(self.array);
		self.engine.use(self.middlewares.array);

		switch (input.multiplicity) {
		case "none":
			input.array.push(null);
			add([], "0");
			break;
		case "single":
			add(input.array, "0");
			break;
		case "multiple":
			for (var index in input.array) {
				add([input.array[index]], index.toString());
			}
			break;
		}

		self.nextTick();
	}

	this.serial.use = function (middleware) {
		self.middleware.serial.default = configure(self.middleware.serial.default, middleware);
		return self;
	}

	this.serial.array = {
		use: function (middleware) {
			self.middleware.serial.array = configure(self.middleware.serial.array, middleware);
			return self;
		}
	}

	this.serial.tasks = {
		use: function (middleware) {
			self.middleware.serial.tasks = configure(self.middleware.serial.tasks, middleware);
			return self;
		}
	}

	this.parallel.use = function (middleware) {
		self.middleware.parallel.default = configure(self.middleware.parallel.default, middleware);
	}

	this.parallel.array = {
		use: function (middleware) {
			self.middleware.parallel.array = configure(self.middleware.parallel.array, middleware);
			return self;
		}
	}

	this.parallel.tasks = {
		use: function (middleware) {
			self.middleware.parallel.tasks = configure(self.middleware.parallel.tasks, middleware);
			return self;
		}
	}

	/* Private members */

	 function clone () {
		var clone = new qsync();

		clone.middleware.serial.default = self.middleware.serial.default;
		clone.middleware.serial.array = self.middleware.serial.array;
		clone.middleware.serial.tasks = self.middleware.serial.tasks;
		clone.middleware.parallel.default = self.middleware.parallel.default;
		clone.middleware.parallel.array = self.middleware.parallel.array;
		clone.middleware.parallel.tasks = self.middleware.parallel.tasks;

		return clone;
	}

	function refine (input) {
		var array, multiplicity, tasks, callback;
		var parameters = [].slice.call(input, 0);

		if (!parameters.length) {
			console.trace("Tasks not found");
			throw "Tasks not found";
		} else {
			var last = parameters[parameters.length - 1];
			var secondLast = parameters.length >= 2 ? parameters[parameters.length - 2] : null;

			if (isTasks(last)) {
				array = parameters;
				tasks = parameters.pop();
				callback = null;
			} else if (last instanceof Function) {
				if (isTasks(secondLast)) {
					array = parameters;
					callback = parameters.pop();
					tasks = parameters.pop();
				} else if (secondLast instanceof Function) {
					array = parameters;
					callback = parameters.pop();
					tasks = [parameters.pop()];
				} else {
					array = parameters;
					tasks = [parameters.pop()];
					callback = null;
				}
			} else {
				console.trace("Last argument has to be either tasks or callback");
				throw "Last argument has to be either tasks or callback";
			}
		}

		if (array.length == 0) {
			multiplicity = "none";
		} else if (array.length == 1) {
			if (array[0] instanceof Array) {
				array = array[0];
				multiplicity = "multiple";
			} else {
				multiplicity = "single";
			}
		}  else if (array.length > 1) {
			multiplicity = "single";
		}

		return {
			"array": array,
			"multiplicity": multiplicity,
			"tasks": tasks,
			"callback": callback
		};

		function isTasks (tasks) {
			if ((!(tasks instanceof Function) &&
				(tasks instanceof Object && Object.keys(tasks).length)) ||
				(tasks instanceof Array && tasks.length)) {
				for (var key in tasks) {
					if (!(tasks[key] instanceof Function)) {
						return false;
					}
				}
				return true;
			} else {
				return false;
			}
		}
	}

	function add (data, label, callback) {
		var tasks = new engine();
		var input = self.parameters;

		tasks.init(self.tasks, self.type);

		if (input.multiplicity == "none") {
			tasks.use(self.middlewares.default);
		} else {
			tasks.use(self.middlewares.tasks);
		}

		self.array.add(label);

		for (var key in input.tasks) {
			tasks.add(input.tasks[key], key.toString(), callback);
		}

		self.engine.add(function (result, callback) {
			tasks.run(data, callback);
		}, label);
	}

	function run () {
		var input = self.parameters;

		self.engine.run([null], function (error, results) {
			if (input.multiplicity == "multiple") {
				input.callback.apply(this, arguments);
			} else {
				if (results) {
					input.callback.apply(this, [error].concat(results));
				} else {
					input.callback(error);
				}
			}
		});
	}

	function configure (source, middleware) {
		return {
			"flow": middleware.run ? middleware : source && source.flow,
			"store": middleware.store ? middleware : source && source.store
		};
	}
}

qsync.sync = require("./serial");
qsync.async = require("./parallel");
qsync.store = require("./store");
qsync.last = require("./last");

module.exports= qsync;
<commit_msg>Bug fixed on array final callback<commit_after>function qsync () {
	var self = this;
	var shim = require("./shim");
	var engine = require("./engine");

	this.middleware = {
		"serial": {
			"default": {},
			"array": {},
			"tasks": {}
		},
		"parallel": {
			"default": {},
			"array": {},
			"tasks": {}
		}
	};

	this.serial = function (array, tasks, callback) {
		var model = clone();

		model.select(model.middleware.serial);
		model.process(arguments);

		return model;
	}

	this.parallel = function (array, tasks, callback) {
		var model = clone();

		model.select(model.middleware.parallel);
		model.process(arguments);

		return model;
	}

	this.queue = function (data, callback) {
		var label, input = self.parameters;
		var label = input.array.length;

		if (input.multiplicity == "single" &&
			input.array.length > 1) {
			input.array = [input.array, data];
		} else {
			input.array.push(data);
		}

		input.multiplicity = "multiple";
		add([data], label.toString(), callback);
		self.nextTick();

		return self;
	}

	this.priority = function (data, priority, callback) {
		var label, input = self.parameters;
		var label = input.array.length;

		if (input.multiplicity == "single" &&
			input.array.length > 1) {
			input.array = [input.array, data];
		} else {
			input.array.push(data);
		}

		input.multiplicity = "multiple";
		add([data], label.toString(), callback);
		self.nextTick();

		return self;
	}

	this.pause = function () {
		if (self.handle) {
			clearImmediate(self.handle);
			self.handle = undefined;
		}

		return self;
	}

	this.resume = function () {
		if (self.handle) {
			clearImmediate(self.handle);
			self.handle = undefined;
		}

		run();
		return self;
	}

	this.nextTick = function () {
		if (!self.handle) {
			self.handle = setImmediate(function () {
				self.handle = undefined;
				run();
			});
		}

		return self;
	}

	this.select = function (middlewares) {
		self.middlewares = middlewares;
	}

	this.process = function (parameters, configure) {
		var input = self.parameters = refine(parameters);

		if (input.tasks instanceof Array) {
			self.type = Array;
		} else if (input.tasks instanceof Function) {
			self.type = Function;
		} else if (input.tasks instanceof Object) {
			self.type = Object;
		}

		self.array = new shim();
		self.tasks = new shim();

		for (var key in input.tasks) {
			self.tasks.add(key.toString());
		}

		self.engine = new engine();
		self.engine.init(self.array);
		self.engine.use(self.middlewares.array);

		switch (input.multiplicity) {
		case "none":
			input.array.push(null);
			add([], "0");
			break;
		case "single":
			add(input.array, "0");
			break;
		case "multiple":
			for (var index in input.array) {
				add([input.array[index]], index.toString());
			}
			break;
		}

		self.nextTick();
	}

	this.serial.use = function (middleware) {
		self.middleware.serial.default = configure(self.middleware.serial.default, middleware);
		return self;
	}

	this.serial.array = {
		use: function (middleware) {
			self.middleware.serial.array = configure(self.middleware.serial.array, middleware);
			return self;
		}
	}

	this.serial.tasks = {
		use: function (middleware) {
			self.middleware.serial.tasks = configure(self.middleware.serial.tasks, middleware);
			return self;
		}
	}

	this.parallel.use = function (middleware) {
		self.middleware.parallel.default = configure(self.middleware.parallel.default, middleware);
	}

	this.parallel.array = {
		use: function (middleware) {
			self.middleware.parallel.array = configure(self.middleware.parallel.array, middleware);
			return self;
		}
	}

	this.parallel.tasks = {
		use: function (middleware) {
			self.middleware.parallel.tasks = configure(self.middleware.parallel.tasks, middleware);
			return self;
		}
	}

	/* Private members */

	 function clone () {
		var clone = new qsync();

		clone.middleware.serial.default = self.middleware.serial.default;
		clone.middleware.serial.array = self.middleware.serial.array;
		clone.middleware.serial.tasks = self.middleware.serial.tasks;
		clone.middleware.parallel.default = self.middleware.parallel.default;
		clone.middleware.parallel.array = self.middleware.parallel.array;
		clone.middleware.parallel.tasks = self.middleware.parallel.tasks;

		return clone;
	}

	function refine (input) {
		var array, multiplicity, tasks, callback;
		var parameters = [].slice.call(input, 0);

		if (!parameters.length) {
			console.trace("Tasks not found");
			throw "Tasks not found";
		} else {
			var last = parameters[parameters.length - 1];
			var secondLast = parameters.length >= 2 ? parameters[parameters.length - 2] : null;

			if (isTasks(last)) {
				array = parameters;
				tasks = parameters.pop();
				callback = null;
			} else if (last instanceof Function) {
				if (isTasks(secondLast)) {
					array = parameters;
					callback = parameters.pop();
					tasks = parameters.pop();
				} else if (secondLast instanceof Function) {
					array = parameters;
					callback = parameters.pop();
					tasks = [parameters.pop()];
				} else {
					array = parameters;
					tasks = [parameters.pop()];
					callback = null;
				}
			} else {
				console.trace("Last argument has to be either tasks or callback");
				throw "Last argument has to be either tasks or callback";
			}
		}

		if (array.length == 0) {
			multiplicity = "none";
		} else if (array.length == 1) {
			if (array[0] instanceof Array) {
				array = array[0];
				multiplicity = "multiple";
			} else {
				multiplicity = "single";
			}
		}  else if (array.length > 1) {
			multiplicity = "single";
		}

		return {
			"array": array,
			"multiplicity": multiplicity,
			"tasks": tasks,
			"callback": callback
		};

		function isTasks (tasks) {
			if ((!(tasks instanceof Function) &&
				(tasks instanceof Object && Object.keys(tasks).length)) ||
				(tasks instanceof Array && tasks.length)) {
				for (var key in tasks) {
					if (!(tasks[key] instanceof Function)) {
						return false;
					}
				}
				return true;
			} else {
				return false;
			}
		}
	}

	function add (data, label, callback) {
		var tasks = new engine();
		var input = self.parameters;

		tasks.init(self.tasks, self.type);

		if (input.multiplicity == "none") {
			tasks.use(self.middlewares.default);
		} else {
			tasks.use(self.middlewares.tasks);
		}

		self.array.add(label);

		for (var key in input.tasks) {
			tasks.add(input.tasks[key], key.toString(), callback);
		}

		self.engine.add(function (callback) {
			var callback = [].slice.call(arguments, 0).pop();

			tasks.run(data, callback);
		}, label);
	}

	function run () {
		var input = self.parameters;

		self.engine.run([], function (error, results) {
			if (input.multiplicity == "multiple") {
				input.callback.apply(this, arguments);
			} else {
				if (results) {
					input.callback.apply(this, [error].concat(results));
				} else {
					input.callback(error);
				}
			}
		});
	}

	function configure (source, middleware) {
		return {
			"flow": middleware.run ? middleware : source && source.flow,
			"store": middleware.store ? middleware : source && source.store
		};
	}
}

qsync.sync = require("./serial");
qsync.async = require("./parallel");
qsync.store = require("./store");
qsync.last = require("./last");

module.exports= qsync;
<|endoftext|>


<commit_before>// The querier for querying Neo4j using the REST API

// dependencies
var _ = require('lomath')
var Promise = require('bluebird')
var reqscraper = require('reqscraper')
var request = reqscraper.req

// statement JSON keys
var skey = ['statement', 'parameters']

/**
 * Generate the statement object from query and optional params.
 * @private
 * @param  {string|Array} query  The query string, or the array of query string and optional params.
 * @param  {JSON} [params] The params JSON for query; taken only if query is a string.
 * @return {JSON}        The statement object
 *
 * @example
 * genStatement(
 * 'CREATE (n {prop})',
 * {
 *  prop: { name: 'a', num: 1 }
 * })
 *
 * // equivalently,
 * genStatement([
 * 'CREATE (n {prop})',
 * {
 *  prop: { name: 'a', num: 1 }
 * }])
 * // => { statement: 'CREATE (n {prop})', parameters: { prop: { name: 'a', num: 1 } } }
 */
function genStatement(query, params) {
  var qpArr = _.isArray(query) ? query : [query, params];
  return _.zipObject(skey, qpArr)
}

/**
 * Generate an array of statements from multiple array-pairs of query and params.
 * @private
 * @param  {...Arrays} ...pairs Array like [query, params]
 * @param  {string} query A query string, if the first argument isn't an array.
 * @param  {JSON} params The parameters JSON to the query string, if the first argument isn't an array.
 * @return {Array}         Array of statements.
 *
 * @example
 * genStatArr(
 * ['q1','p1'],
 * ['q2','p2']
 * )
 * // => [ { statement: 'q1', parameters: 'p1' },
 * // { statement: 'q2', parameters: 'p2' } ]
 *
 * genStatArr([
 * ['q1','p1'],
 * ['q2','p2']
 * ])
 * // => [ { statement: 'q1', parameters: 'p1' },
 * // { statement: 'q2', parameters: 'p2' } ]
 *
 * genStatArr('q1', 'p1')
 * // => [ { statement: 'q1', parameters: 'p1' } ]
 */
function genStatArr() {
  // return _.isArray(arguments[0]) ? _.map(arguments, genStatement) : [genStatement(_.toArray(arguments))]
  // if the first arg is array
  if (_.isArray(arguments[0])) {
    // ([['q1', 'p1'], ['q2', 'p2'], ...])
    if (_.isArray(arguments[0][0])) {
      return _.map(arguments[0], genStatement)
        // (['q1', 'p1'], ['q2', 'p2'], ...)
    } else {
      return _.map(arguments, genStatement)
    }
  } else {
    // args is in ('q1', 'p1') form
    return [genStatement(_.toArray(arguments))]
  }
}

/**
 * POST a comitting query to the db.
 * @private
 * @param  {Array}   statArr  Array of statement objects.
 * @param  {Function} callback Function with (err, res, body) args for the query result.
 * @return {Promise}  A promise object resolved with the query results from the request module.
 */
function postQuery(statArr) {
  var options = {
      method: 'POST',
      baseUrl: this.NEO4J_BASEURL,
      url: this.NEO4J_ENDPT,
      headers: {
        'Accept': 'application/json; charset=UTF-8',
        'Content-type': 'application/json'
      },
      json: {
        statements: statArr
          // [{statement: query, parameters: params},
          // {statement: query, parameters: params}]
      }
    }
    // call to the db server, returns a Promise
  return request(options).then(resolver).catch(_.identity)
}

/**
 * Resolver function to chain Promise to resolve results or reject errors. Used in postQuery.
 * @private
 * @param  {JSON} obj Returned from the neo4j server.
 * @return {Promise}     That resolves results or rejects errors.
 */
function resolver(obj) {
  return new Promise(function(resolve, reject) {
    _.isEmpty(obj.results) ? reject(obj.errors) : resolve(obj.results);
  })
}

/**
 * Queries Neo4j with arrays of pairs of query and optional params. Is the right-composition of genStatArr and postQuery.
 * This is is query() method with full power, i.e. no query-string cleaning. Use responsibly.
 * The high level add/get methods call this internall with query-string cleaning to prevent SQL injection.
 * The query cleaning algorithm is (proven( based on the CFG parse tree and logic. Refer to isLegalSentence().
 * A sentence is a clause starting with the following query operation specifier: `WHERE,SET,REMOVE,RETURN,DELETE,DETACH DELETE,SHORTESTPATH,ALLSHORTESTPATHS`
 * The sentence also consists of variables and operators <op>. The permissible operators by this algorithm are `AND,OR,XOR,NOT,=,<>,<,>,<=,>=,IS NULL,IS NOT NULL,+,+=,=~,EXISTS,nodes,labels,keys,",",=,ORDER BY,LIMIT,COUNT,sum,avg,percentileDisc,percentileCont,stdev,stdevp,max,min,collect,DISTINCT,nodes,labels,keys`
 *
 * @param  {...Arrays}   ...Pairs  Of queries and optional params.
 * @param  {string} query A query string, if the first argument isn't an array.
 * @param  {JSON} params The parameters JSON to the query string, if the first argument isn't an array.
 * @return {Promise}  A promise object resolved with the query results from the request module.
 *
 * @example
 * Normal query string
 * query('MATCH (n:Alphabet) DETACH DELETE (n)')
 * .then(KB.log)
 * // => {"results":[{"columns":[],"data":[]}],"errors":[]}
 * // Deleted all Alphabet nodes and relations
 *
 * // query with params, creates multiple nodes at once
 * query(
 * ['CREATE (n:Alphabet {prop}) RETURN n',
 * {
 *  prop: [{ name: 'a', num: 1}, { name: 'b', num: 2 }]
 * }]
 * ).then(KB.log)
 * // => {"results":[{"columns":["n"],"data":[{"row":[{"num":1,"name":"a"}]},{"row":[{"num":2,"name":"b"}]}]}],"errors":[]}
 * // Added nodes 'a', 'b' to the graph
 *
 * // multiple queries at once
 * query(
 * ['CREATE (n:Alphabet {prop}) RETURN n', { prop: [{ name: 'c', num: 3}, { name: 'd', num: 4 }] }],
 * ["MATCH (c),(d) WHERE c.name='c' AND d.name='d' CREATE UNIQUE (c)-[r:Next]->(d) RETURN r"]
 * ).then(KB.log)
 *
 * // equivalently can nest under one array
 * query(
 * [
 * ['CREATE (n:Alphabet {prop}) RETURN n', { prop: [{ name: 'c', num: 3}, { name: 'd', num: 4 }] }],
 * ["MATCH (c),(d) WHERE c.name='c' AND d.name='d' CREATE UNIQUE (c)-[r:Next]->(d) RETURN r"]
 * ]
 * ).then(KB.log)
 * // => {"results":[{"columns":["n"],"data":[{"row":[{"num":3,"name":"c"}]},{"row":[{"num":4,"name":"d"}]}]},{"columns":["r"],"data":[{"row":[{}]}]}],"errors":[]}
 * Created nodes 'c', 'd', then added an edge (c)->(d)
 *
 */
var query = _.flow(genStatArr, postQuery)

// sample calls
// query('MATCH (n:Alphabet) DETACH DELETE (n)')
// .then(KB.log)

// query(
// ['CREATE (n:Alphabet {prop}) RETURN n',
// {
//  prop: [{ name: 'a', num: 1}, { name: 'b', num: 2 }]
// }]
// ).then(KB.log)

// query(
// [
// ['CREATE (n:Alphabet {prop}) RETURN n',
// {
//  prop: [{ name: 'a', num: 1}, { name: 'b', num: 2 }]
// }]
// ]
// ).then(KB.log)

// query(
// ['CREATE (n:Alphabet {prop}) RETURN n',
// {
//  prop: [{ name: 'c', num: 3}, { name: 'd', num: 4 }]
// }],
// ["MATCH (c),(d) WHERE c.name='c' AND d.name='d' CREATE UNIQUE (c)-[r:Next]->(d) RETURN r"]
// ).then(KB.log)

/**
 * This is the exported wrapper function: init the necessary params, then return the query. The fields are set to neo4j defaults if not supplied.
 * @private
 * @param  {JSON} options An optional JSON with the fields {NEO4J_AUTH='neo4j:neo4j', NEO4J_HOST='localhost', NEO4J_PORT-'7474'} containing username:password, the host address, and port number for neo4j
 * @return {Function}            The query function.
 */
var queryWrap = function(options) {
  options = options || {}
  this.NEO4J_AUTH = options.NEO4J_AUTH || 'neo4j:neo4j'
  this.NEO4J_HOST = options.NEO4J_HOST || 'localhost'
  this.NEO4J_PORT = options.NEO4J_PORT || '7474'
  this.NEO4J_BASEURL = 'http://' + this.NEO4J_AUTH + '@' + this.NEO4J_HOST + ':' + this.NEO4J_PORT + '/'
  this.NEO4J_ENDPT = 'db/data/transaction/commit'

  return query;
}

// export for usage
module.exports = queryWrap
<commit_msg>use err in bluebird reject, prevent err<commit_after>// The querier for querying Neo4j using the REST API

// dependencies
var _ = require('lomath')
var Promise = require('bluebird')
var reqscraper = require('reqscraper')
var request = reqscraper.req

// statement JSON keys
var skey = ['statement', 'parameters']

/**
 * Generate the statement object from query and optional params.
 * @private
 * @param  {string|Array} query  The query string, or the array of query string and optional params.
 * @param  {JSON} [params] The params JSON for query; taken only if query is a string.
 * @return {JSON}        The statement object
 *
 * @example
 * genStatement(
 * 'CREATE (n {prop})',
 * {
 *  prop: { name: 'a', num: 1 }
 * })
 *
 * // equivalently,
 * genStatement([
 * 'CREATE (n {prop})',
 * {
 *  prop: { name: 'a', num: 1 }
 * }])
 * // => { statement: 'CREATE (n {prop})', parameters: { prop: { name: 'a', num: 1 } } }
 */
function genStatement(query, params) {
  var qpArr = _.isArray(query) ? query : [query, params];
  return _.zipObject(skey, qpArr)
}

/**
 * Generate an array of statements from multiple array-pairs of query and params.
 * @private
 * @param  {...Arrays} ...pairs Array like [query, params]
 * @param  {string} query A query string, if the first argument isn't an array.
 * @param  {JSON} params The parameters JSON to the query string, if the first argument isn't an array.
 * @return {Array}         Array of statements.
 *
 * @example
 * genStatArr(
 * ['q1','p1'],
 * ['q2','p2']
 * )
 * // => [ { statement: 'q1', parameters: 'p1' },
 * // { statement: 'q2', parameters: 'p2' } ]
 *
 * genStatArr([
 * ['q1','p1'],
 * ['q2','p2']
 * ])
 * // => [ { statement: 'q1', parameters: 'p1' },
 * // { statement: 'q2', parameters: 'p2' } ]
 *
 * genStatArr('q1', 'p1')
 * // => [ { statement: 'q1', parameters: 'p1' } ]
 */
function genStatArr() {
  // return _.isArray(arguments[0]) ? _.map(arguments, genStatement) : [genStatement(_.toArray(arguments))]
  // if the first arg is array
  if (_.isArray(arguments[0])) {
    // ([['q1', 'p1'], ['q2', 'p2'], ...])
    if (_.isArray(arguments[0][0])) {
      return _.map(arguments[0], genStatement)
        // (['q1', 'p1'], ['q2', 'p2'], ...)
    } else {
      return _.map(arguments, genStatement)
    }
  } else {
    // args is in ('q1', 'p1') form
    return [genStatement(_.toArray(arguments))]
  }
}

/**
 * POST a comitting query to the db.
 * @private
 * @param  {Array}   statArr  Array of statement objects.
 * @param  {Function} callback Function with (err, res, body) args for the query result.
 * @return {Promise}  A promise object resolved with the query results from the request module.
 */
function postQuery(statArr) {
  var options = {
      method: 'POST',
      baseUrl: this.NEO4J_BASEURL,
      url: this.NEO4J_ENDPT,
      headers: {
        'Accept': 'application/json; charset=UTF-8',
        'Content-type': 'application/json'
      },
      json: {
        statements: statArr
          // [{statement: query, parameters: params},
          // {statement: query, parameters: params}]
      }
    }
    // call to the db server, returns a Promise
  return request(options).then(resolver).catch(_.identity)
}

/**
 * Resolver function to chain Promise to resolve results or reject errors. Used in postQuery.
 * @private
 * @param  {JSON} obj Returned from the neo4j server.
 * @return {Promise}     That resolves results or rejects errors.
 */
function resolver(obj) {
  return new Promise(function(resolve, reject) {
    _.isEmpty(obj.results) ? reject(new Error(obj.errors)) : resolve(obj.results);
  })
}

/**
 * Queries Neo4j with arrays of pairs of query and optional params. Is the right-composition of genStatArr and postQuery.
 * This is is query() method with full power, i.e. no query-string cleaning. Use responsibly.
 * The high level add/get methods call this internall with query-string cleaning to prevent SQL injection.
 * The query cleaning algorithm is (proven( based on the CFG parse tree and logic. Refer to isLegalSentence().
 * A sentence is a clause starting with the following query operation specifier: `WHERE,SET,REMOVE,RETURN,DELETE,DETACH DELETE,SHORTESTPATH,ALLSHORTESTPATHS`
 * The sentence also consists of variables and operators <op>. The permissible operators by this algorithm are `AND,OR,XOR,NOT,=,<>,<,>,<=,>=,IS NULL,IS NOT NULL,+,+=,=~,EXISTS,nodes,labels,keys,",",=,ORDER BY,LIMIT,COUNT,sum,avg,percentileDisc,percentileCont,stdev,stdevp,max,min,collect,DISTINCT,nodes,labels,keys`
 *
 * @param  {...Arrays}   ...Pairs  Of queries and optional params.
 * @param  {string} query A query string, if the first argument isn't an array.
 * @param  {JSON} params The parameters JSON to the query string, if the first argument isn't an array.
 * @return {Promise}  A promise object resolved with the query results from the request module.
 *
 * @example
 * Normal query string
 * query('MATCH (n:Alphabet) DETACH DELETE (n)')
 * .then(KB.log)
 * // => {"results":[{"columns":[],"data":[]}],"errors":[]}
 * // Deleted all Alphabet nodes and relations
 *
 * // query with params, creates multiple nodes at once
 * query(
 * ['CREATE (n:Alphabet {prop}) RETURN n',
 * {
 *  prop: [{ name: 'a', num: 1}, { name: 'b', num: 2 }]
 * }]
 * ).then(KB.log)
 * // => {"results":[{"columns":["n"],"data":[{"row":[{"num":1,"name":"a"}]},{"row":[{"num":2,"name":"b"}]}]}],"errors":[]}
 * // Added nodes 'a', 'b' to the graph
 *
 * // multiple queries at once
 * query(
 * ['CREATE (n:Alphabet {prop}) RETURN n', { prop: [{ name: 'c', num: 3}, { name: 'd', num: 4 }] }],
 * ["MATCH (c),(d) WHERE c.name='c' AND d.name='d' CREATE UNIQUE (c)-[r:Next]->(d) RETURN r"]
 * ).then(KB.log)
 *
 * // equivalently can nest under one array
 * query(
 * [
 * ['CREATE (n:Alphabet {prop}) RETURN n', { prop: [{ name: 'c', num: 3}, { name: 'd', num: 4 }] }],
 * ["MATCH (c),(d) WHERE c.name='c' AND d.name='d' CREATE UNIQUE (c)-[r:Next]->(d) RETURN r"]
 * ]
 * ).then(KB.log)
 * // => {"results":[{"columns":["n"],"data":[{"row":[{"num":3,"name":"c"}]},{"row":[{"num":4,"name":"d"}]}]},{"columns":["r"],"data":[{"row":[{}]}]}],"errors":[]}
 * Created nodes 'c', 'd', then added an edge (c)->(d)
 *
 */
var query = _.flow(genStatArr, postQuery)

// sample calls
// query('MATCH (n:Alphabet) DETACH DELETE (n)')
// .then(KB.log)

// query(
// ['CREATE (n:Alphabet {prop}) RETURN n',
// {
//  prop: [{ name: 'a', num: 1}, { name: 'b', num: 2 }]
// }]
// ).then(KB.log)

// query(
// [
// ['CREATE (n:Alphabet {prop}) RETURN n',
// {
//  prop: [{ name: 'a', num: 1}, { name: 'b', num: 2 }]
// }]
// ]
// ).then(KB.log)

// query(
// ['CREATE (n:Alphabet {prop}) RETURN n',
// {
//  prop: [{ name: 'c', num: 3}, { name: 'd', num: 4 }]
// }],
// ["MATCH (c),(d) WHERE c.name='c' AND d.name='d' CREATE UNIQUE (c)-[r:Next]->(d) RETURN r"]
// ).then(KB.log)

/**
 * This is the exported wrapper function: init the necessary params, then return the query. The fields are set to neo4j defaults if not supplied.
 * @private
 * @param  {JSON} options An optional JSON with the fields {NEO4J_AUTH='neo4j:neo4j', NEO4J_HOST='localhost', NEO4J_PORT-'7474'} containing username:password, the host address, and port number for neo4j
 * @return {Function}            The query function.
 */
var queryWrap = function(options) {
  options = options || {}
  this.NEO4J_AUTH = options.NEO4J_AUTH || 'neo4j:neo4j'
  this.NEO4J_HOST = options.NEO4J_HOST || 'localhost'
  this.NEO4J_PORT = options.NEO4J_PORT || '7474'
  this.NEO4J_BASEURL = 'http://' + this.NEO4J_AUTH + '@' + this.NEO4J_HOST + ':' + this.NEO4J_PORT + '/'
  this.NEO4J_ENDPT = 'db/data/transaction/commit'

  return query;
}

// export for usage
module.exports = queryWrap
<|endoftext|>


<commit_before>'use strict';

const _ = require('lodash');
const P = require('bluebird');
const EventEmitter = require('events').EventEmitter;
const Logger = require('glib').Logger;
const log = new Logger(['query']);
const joi = require('joi');
const Stream = require('stream').Stream;
const QueryResult = require('./query-result').QueryResult;
const FieldConfigurator = require('./field-configurator').FieldConfigurator;

var $ = require('highland');

const SCHEMA = joi.object({
    preHandlers: joi.array().items(joi.func()).default([]),
    postHandlers: joi.array().items(joi.func()).default([]),
    throughHandlers: joi.array().items(joi.alternatives([ joi.func(), joi.object().type(Stream) ])).default([]),
    handler: joi.func().required(),
    payload: joi.any(),
    user: joi.any(),
    fields: joi.object(),
    limit: joi.number().integer().default(-1),
    language: joi.string(),
    options: joi.object().default({}),
    params: joi.alternatives([ joi.object(), joi.array() ]).default({}).empty(null),
    plugins: joi.array().items(joi.func()).default([]),
    listeners: joi.array().items(joi.array().items(joi.string(), joi.func())).default([])
});

/**
 * @extends EventEmitter
 */
class Query extends EventEmitter {
    constructor() {
        super();

        // properties declared here for jsdoc purposes
        this.limit = -1;
        this.preHandlers = this.postHandlers = this.throughHandlers = [];
        this.options = {};
        this.params = {};
    }

    /**
     * Configures the query from a defn descriptor
     * @param {{}} defn the definition descriptor
     * @return {Query}
     */
    configure(defn) {
        defn = joi.attempt(defn, SCHEMA);
         _.assign(this, _.omit(defn, 'plugins', 'listeners'));
        defn.plugins.forEach(p => p(this));
        defn.listeners.forEach(l => this.on(l[0], l[1]));
        return this;
    }

    /**
     * Adds a through stream handler
     * @param through
     * @return {Query}
     */
    through(through) {
        this.throughHandlers = this.throughHandlers.concat(through);
        return this;
    }

    /**
     * Adds a pre handler
     * @param handler
     * @return {Query}
     */
    pre(handler) {
        this.preHandlers = this.preHandlers.concat(handler);
        return this;
    }

    /**
     * Adds a post handler
     * @param handler
     * @return {Query}
     */
    post(handler) {
        this.postHandlers = this.postHandlers.concat(handler);
        return this;
    }

    /**
     * Registers a plugin with the query
     * @param {function} plugin the plugin registration function
     * @return {Query}
     */
    use(plugin) {
        plugin(this);

        return this;
    }

    /**
     * Configures a field
     * @param {string} field the field name
     * @param {function=} callback an optional callback function for accessing the field configurator
     * @example
     * // with a callback
     * qr
     *  .field('location', fc => fc.type('geo_point').label('Coordinates'))
     *  .field('firstName', fc => fc.label('First Name'));
     *  .field('lastName', fc => fc.label('Last Name'));
     *
     *  // without a callback
     *  qr.field('location').type('geo_point');
     *  qr.field('firstName').label('First Name');
     *  qr.field('lastName').label('Last Name');
     *
     * @return {FieldConfigurator|Query}
     */
    field(field, callback) {
        const fc = new FieldConfigurator(this.fields, field);
        if (callback) {
            callback(fc);
            return this;
        }

        return fc;
    }

    /**
     * Creates a promise chain for invoking interceptors, ignoring any return value
     * @param {[]} interceptors the interceptor functions
     * @return {Function}
     * @private
     */
    _tapChain(interceptors) {
        return function(value) {
            return interceptors.reduce(function (p, interceptor) {
                return p.tap(interceptor);
            }, P.resolve(value));
        };
    }

    /**
     * Invokes the query's handler function and is responsible for initializing the result
     * @param {function(Query, function)} handler the handler function
     * @return {Promise.<QueryResult>}
     * @private
     */
    _invokeHandler(handler) {
        const self = this;

        // short circuit the handler if already cancelled
        if (this.cancelled) return new QueryResult(this);

        return new P(function (resolve, reject) {

            // the reply function passed to the query handler
            const reply = _.once(function (err, data) {

                // errors thrown in the handler will have same effect
                if (err && err instanceof Error) return reject(err);

                // init the result
                const qr = new QueryResult(self, data || err);

                // give the handler a moment to configure field metadata before processing the results
                setImmediate(() => resolve(qr));

                // return the configuration shim
                return qr.shim();
            });

            self.on('cancel', reply);
            handler(self, reply);
        });
    }

    _setResult(result) {
        this.result = result;
    }

    /**
     * Runs the query with the supplied parameters, returning a promise to a QueryResult
     * @return {Promise.<QueryResult>}
     */
    execute() {
        if (this._promise) return this._promise;

        if (!this.handler) return P.reject('No handler defined');

        return this._promise = P.bind(this)
            .tap(() => log.info('Executing query'))
            .tap(() => this.emit('execute'))
            .tap(() => this._tapChain(this.preHandlers)(this))
            .then(() => this._invokeHandler(this.handler))
            .tap(r => this._setResult(r))
            .tap(r => this._tapChain(this.postHandlers)(r))
            .tap(r => this.emit('result', r));
    }

    /**
     * Cancels a running query. NOTE: this method is overridden to call QueryResult.cancel() once the reply() callback
     * is called.
     *
     * Handlers should detect early termination of queries in two ways:
     *
     *

     *     function handler(query, reply) {
     *          let conn = getConnection();
     *
     *          // a cancellation event on query indicates that the query is still running (we haven't called reply yet)
     *          query.on('cancel', () => conn.end())
     *
     *          conn.query(query.payload)
     *              .then(function(result) {
     *                  // an end event on the reply is emitted when the results are consumed or the client cancels
     *                  // the query result
     *                  reply(result.stream()).on('end', () => conn.end());
     *              });
     *     }
     *

     */
    cancel() {
        this.cancelled = true;
        this.emit('cancel');
        if (this.result) this.result.cancel();
        return this;
    }

    /**
     * Emits a progress event
     * @param progress
     * @return {boolean|*}
     */
    progress(progress) {
        return this.emit('progress', progress);
    }

    /**
     * Executes the query, synchronously returning a stream bound (eventually) to the result's stream
     * @return {Stream}
     */
    stream() {
        return $.sequence(this.execute().then(r => r.stream()));
    }

    /**
     * Executes the query returning a promise that resolves to an array containing all results
     * @return {Promise.<{}[]>}
     */
    toArray() {
        return this.execute().then(r => r.toArray());
    }
}

exports.Query = Query;<commit_msg>fix bug with assigning params during execute()<commit_after>'use strict';

const _ = require('lodash');
const P = require('bluebird');
const EventEmitter = require('events').EventEmitter;
const Logger = require('glib').Logger;
const log = new Logger(['query']);
const joi = require('joi');
const Stream = require('stream').Stream;
const QueryResult = require('./query-result').QueryResult;
const FieldConfigurator = require('./field-configurator').FieldConfigurator;

var $ = require('highland');

const SCHEMA = joi.object({
    preHandlers: joi.array().items(joi.func()).default([]),
    postHandlers: joi.array().items(joi.func()).default([]),
    throughHandlers: joi.array().items(joi.alternatives([ joi.func(), joi.object().type(Stream) ])).default([]),
    handler: joi.func().required(),
    payload: joi.any(),
    user: joi.any(),
    fields: joi.object(),
    limit: joi.number().integer().default(-1),
    language: joi.string(),
    options: joi.object().default({}),
    params: joi.alternatives([ joi.object(), joi.array() ]).default({}).empty(null),
    plugins: joi.array().items(joi.func()).default([]),
    listeners: joi.array().items(joi.array().items(joi.string(), joi.func())).default([])
});

/**
 * @extends EventEmitter
 */
class Query extends EventEmitter {
    constructor() {
        super();

        // properties declared here for jsdoc purposes
        this.limit = -1;
        this.preHandlers = this.postHandlers = this.throughHandlers = [];
        this.options = {};
        this.params = {};
    }

    /**
     * Configures the query from a defn descriptor
     * @param {{}} defn the definition descriptor
     * @return {Query}
     */
    configure(defn) {
        defn = joi.attempt(defn, SCHEMA);
         _.assign(this, _.omit(defn, 'plugins', 'listeners'));
        defn.plugins.forEach(p => p(this));
        defn.listeners.forEach(l => this.on(l[0], l[1]));
        return this;
    }

    /**
     * Adds a through stream handler
     * @param through
     * @return {Query}
     */
    through(through) {
        this.throughHandlers = this.throughHandlers.concat(through);
        return this;
    }

    /**
     * Adds a pre handler
     * @param handler
     * @return {Query}
     */
    pre(handler) {
        this.preHandlers = this.preHandlers.concat(handler);
        return this;
    }

    /**
     * Adds a post handler
     * @param handler
     * @return {Query}
     */
    post(handler) {
        this.postHandlers = this.postHandlers.concat(handler);
        return this;
    }

    /**
     * Registers a plugin with the query
     * @param {function} plugin the plugin registration function
     * @return {Query}
     */
    use(plugin) {
        plugin(this);

        return this;
    }

    /**
     * Configures a field
     * @param {string} field the field name
     * @param {function=} callback an optional callback function for accessing the field configurator
     * @example
     * // with a callback
     * qr
     *  .field('location', fc => fc.type('geo_point').label('Coordinates'))
     *  .field('firstName', fc => fc.label('First Name'));
     *  .field('lastName', fc => fc.label('Last Name'));
     *
     *  // without a callback
     *  qr.field('location').type('geo_point');
     *  qr.field('firstName').label('First Name');
     *  qr.field('lastName').label('Last Name');
     *
     * @return {FieldConfigurator|Query}
     */
    field(field, callback) {
        const fc = new FieldConfigurator(this.fields, field);
        if (callback) {
            callback(fc);
            return this;
        }

        return fc;
    }

    /**
     * Creates a promise chain for invoking interceptors, ignoring any return value
     * @param {[]} interceptors the interceptor functions
     * @return {Function}
     * @private
     */
    _tapChain(interceptors) {
        return function(value) {
            return interceptors.reduce(function (p, interceptor) {
                return p.tap(interceptor);
            }, P.resolve(value));
        };
    }

    /**
     * Invokes the query's handler function and is responsible for initializing the result
     * @param {function(Query, function)} handler the handler function
     * @return {Promise.<QueryResult>}
     * @private
     */
    _invokeHandler(handler) {
        const self = this;

        // short circuit the handler if already cancelled
        if (this.cancelled) return new QueryResult(this);

        return new P(function (resolve, reject) {

            // the reply function passed to the query handler
            const reply = _.once(function (err, data) {

                // errors thrown in the handler will have same effect
                if (err && err instanceof Error) return reject(err);

                // init the result
                const qr = new QueryResult(self, data || err);

                // give the handler a moment to configure field metadata before processing the results
                setImmediate(() => resolve(qr));

                // return the configuration shim
                return qr.shim();
            });

            self.on('cancel', reply);
            handler(self, reply);
        });
    }

    _setResult(result) {
        this.result = result;
    }

    /**
     * Runs the query with the supplied parameters, returning a promise to a QueryResult
     * @param {*} params runtime query params
     * @return {Promise.<QueryResult>}
     */
    execute(params) {
        this.params = params || {};

        if (this._promise) return this._promise;

        if (!this.handler) return P.reject('No handler defined');

        return this._promise = P.bind(this)
            .tap(() => log.info('Executing query'))
            .tap(() => this.emit('execute'))
            .tap(() => this._tapChain(this.preHandlers)(this))
            .then(() => this._invokeHandler(this.handler))
            .tap(r => this._setResult(r))
            .tap(r => this._tapChain(this.postHandlers)(r))
            .tap(r => this.emit('result', r));
    }

    /**
     * Cancels a running query. NOTE: this method is overridden to call QueryResult.cancel() once the reply() callback
     * is called.
     *
     * Handlers should detect early termination of queries in two ways:
     *
     *

     *     function handler(query, reply) {
     *          let conn = getConnection();
     *
     *          // a cancellation event on query indicates that the query is still running (we haven't called reply yet)
     *          query.on('cancel', () => conn.end())
     *
     *          conn.query(query.payload)
     *              .then(function(result) {
     *                  // an end event on the reply is emitted when the results are consumed or the client cancels
     *                  // the query result
     *                  reply(result.stream()).on('end', () => conn.end());
     *              });
     *     }
     *

     */
    cancel() {
        this.cancelled = true;
        this.emit('cancel');
        if (this.result) this.result.cancel();
        return this;
    }

    /**
     * Emits a progress event
     * @param progress
     * @return {boolean|*}
     */
    progress(progress) {
        return this.emit('progress', progress);
    }

    /**
     * Executes the query, synchronously returning a stream bound (eventually) to the result's stream
     * @return {Stream}
     */
    stream() {
        return $.sequence(this.execute().then(r => r.stream()));
    }

    /**
     * Executes the query returning a promise that resolves to an array containing all results
     * @return {Promise.<{}[]>}
     */
    toArray() {
        return this.execute().then(r => r.toArray());
    }
}

exports.Query = Query;<|endoftext|>


<commit_before>var _ = require('lodash'),
    query = module.exports = {};

query.createValueMatcher = function (value) {
    if (typeof value === 'function') {
        return value;
    } else if (_.isRegExp(value)) {
        return function matchRegExp(obj) {
            return value.test(obj);
        };
    } else if (_.isArray(value)) {
        return function matchArrayMember(obj) {
            return value.includes(obj);
        };
    } else if (Buffer.isBuffer(value)) {
        return function matchBuffer(obj) {
            var i;
            for (i = 0 ; i < obj.length ; i += 1) {
                if (obj[i] !== value[i]) {
                    return false;
                }
            }
            return i === value.length;
        };
    } else if (typeof value === 'object' && value !== null && !value.isAsset && !value.isRelation) {
        return query.queryObjToMatcherFunction(value);
    } else { // Asset or relation or primitive value
        return function matchEqual(obj) {
            return obj === value;
        };
    }
};

query.queryObjToMatcherFunction = function (queryObj) {
    if (typeof queryObj === 'function') {
        return queryObj;
    }
    var matchers = _.map(queryObj || {}, function (value, key) {
        var valueMatcher = query.createValueMatcher(value);
        return function matchPropertyValue(obj) {
            return obj && valueMatcher(obj[key]);
        };
    });
    if (matchers.length === 0) {
        // Make sure that a everything is matched if queryObj is falsy, undefined or {}
        // Amazingly, this is a useful special case.
        return function matchEverything() {
            return true;
        };
    } else if (matchers.length === 1) {
        return matchers[0];
    } else {
        return function (obj) {
            return matchers.every(function (matcher) {
                return matcher(obj);
            });
        };
    }
};

query.createPrefixMatcher = function (prefix) {
    return function (value) {
        return typeof value === 'string' && value.startsWith(prefix);
    };
};

query.queryAssetGraph = function (assetGraph, objType, queryObj, candidates) {
    queryObj = queryObj || {};
    var filters = [];
    if (typeof queryObj === 'function') {
        filters.push(queryObj);
    } else {
        _.each(queryObj || {}, function (fieldQueryObj, fieldName) {
            if (objType === 'asset' && (fieldName === 'incoming' || fieldName === 'outgoing')) {
                filters.push(function (asset) {
                    var relationQueryObj = _.clone(fieldQueryObj);
                    relationQueryObj[fieldName === 'incoming' ? 'to' : 'from'] = asset;
                    return assetGraph.findRelations(relationQueryObj).length > 0;
                });
            }
        });
        var filteredQueryObj = {};
        for (var propertyName in queryObj) {
            if (propertyName !== 'incoming' && propertyName !== 'outgoing') {
                filteredQueryObj[propertyName] = queryObj[propertyName];
            }
        }
        filters.unshift(query.queryObjToMatcherFunction(filteredQueryObj));
    }
    var result = candidates || assetGraph['_' + objType + 's'];

    for (const filter of filters) {
        result = result.filter(filter);
    }
    return result;
};

// Helpers that allow queries like:
// assetGraph.findRelations({
//    from: {
//        type: query.not(['Html', 'Css'])
//    }
// });

query.not = function (value) {
    // Warn against subtle mistakes like query.not('HtmlAnchor', 'HtmlImage') which would otherwise silently ignore the second argument.
    if (arguments.length > 1) {
        throw new Error('query.not: More than one argument provided, did you mean to pass an array?');
    }
    var valueMatcher = query.createValueMatcher(value);
    return function (obj) {
        return !valueMatcher(obj);
    };
};

query.or = function () { // ...
    if (arguments.length < 2) {
        throw new Error('query.or: Two or more arguments must be provided');
    }
    var valueMatchers = _.toArray(arguments).map(query.createValueMatcher);
    return function or(obj) {
        return valueMatchers.some(function (matcher) {
            return matcher(obj);
        });
    };
};

query.and = function () { // ...
    if (arguments.length < 2) {
        throw new Error('query.and: Two or more arguments must be provided');
    }
    var valueMatchers = _.toArray(arguments).map(query.createValueMatcher);
    return function and(obj) {
        return valueMatchers.every(function (matcher) {
            return matcher(obj);
        });
    };
};
<commit_msg>Use Array.isArray and Array.from instead of the lodash equivalents<commit_after>var _ = require('lodash'),
    query = module.exports = {};

query.createValueMatcher = function (value) {
    if (typeof value === 'function') {
        return value;
    } else if (_.isRegExp(value)) {
        return function matchRegExp(obj) {
            return value.test(obj);
        };
    } else if (Array.isArray(value)) {
        return function matchArrayMember(obj) {
            return value.includes(obj);
        };
    } else if (Buffer.isBuffer(value)) {
        return function matchBuffer(obj) {
            var i;
            for (i = 0 ; i < obj.length ; i += 1) {
                if (obj[i] !== value[i]) {
                    return false;
                }
            }
            return i === value.length;
        };
    } else if (typeof value === 'object' && value !== null && !value.isAsset && !value.isRelation) {
        return query.queryObjToMatcherFunction(value);
    } else { // Asset or relation or primitive value
        return function matchEqual(obj) {
            return obj === value;
        };
    }
};

query.queryObjToMatcherFunction = function (queryObj) {
    if (typeof queryObj === 'function') {
        return queryObj;
    }
    var matchers = _.map(queryObj || {}, function (value, key) {
        var valueMatcher = query.createValueMatcher(value);
        return function matchPropertyValue(obj) {
            return obj && valueMatcher(obj[key]);
        };
    });
    if (matchers.length === 0) {
        // Make sure that a everything is matched if queryObj is falsy, undefined or {}
        // Amazingly, this is a useful special case.
        return function matchEverything() {
            return true;
        };
    } else if (matchers.length === 1) {
        return matchers[0];
    } else {
        return function (obj) {
            return matchers.every(function (matcher) {
                return matcher(obj);
            });
        };
    }
};

query.createPrefixMatcher = function (prefix) {
    return function (value) {
        return typeof value === 'string' && value.startsWith(prefix);
    };
};

query.queryAssetGraph = function (assetGraph, objType, queryObj, candidates) {
    queryObj = queryObj || {};
    var filters = [];
    if (typeof queryObj === 'function') {
        filters.push(queryObj);
    } else {
        _.each(queryObj || {}, function (fieldQueryObj, fieldName) {
            if (objType === 'asset' && (fieldName === 'incoming' || fieldName === 'outgoing')) {
                filters.push(function (asset) {
                    var relationQueryObj = _.clone(fieldQueryObj);
                    relationQueryObj[fieldName === 'incoming' ? 'to' : 'from'] = asset;
                    return assetGraph.findRelations(relationQueryObj).length > 0;
                });
            }
        });
        var filteredQueryObj = {};
        for (var propertyName in queryObj) {
            if (propertyName !== 'incoming' && propertyName !== 'outgoing') {
                filteredQueryObj[propertyName] = queryObj[propertyName];
            }
        }
        filters.unshift(query.queryObjToMatcherFunction(filteredQueryObj));
    }
    var result = candidates || assetGraph['_' + objType + 's'];

    for (const filter of filters) {
        result = result.filter(filter);
    }
    return result;
};

// Helpers that allow queries like:
// assetGraph.findRelations({
//    from: {
//        type: query.not(['Html', 'Css'])
//    }
// });

query.not = function (value) {
    // Warn against subtle mistakes like query.not('HtmlAnchor', 'HtmlImage') which would otherwise silently ignore the second argument.
    if (arguments.length > 1) {
        throw new Error('query.not: More than one argument provided, did you mean to pass an array?');
    }
    var valueMatcher = query.createValueMatcher(value);
    return function (obj) {
        return !valueMatcher(obj);
    };
};

query.or = function () { // ...
    if (arguments.length < 2) {
        throw new Error('query.or: Two or more arguments must be provided');
    }
    var valueMatchers = Array.from(arguments).map(query.createValueMatcher);
    return function or(obj) {
        return valueMatchers.some(function (matcher) {
            return matcher(obj);
        });
    };
};

query.and = function () { // ...
    if (arguments.length < 2) {
        throw new Error('query.and: Two or more arguments must be provided');
    }
    var valueMatchers = Array.from(arguments).map(query.createValueMatcher);
    return function and(obj) {
        return valueMatchers.every(function (matcher) {
            return matcher(obj);
        });
    };
};
<|endoftext|>


<commit_before>var uuid = require('node-uuid');
var job  = require('./job').job;

function queue(name, client, worker) {
	this.name   = name;
	this.client = client;
	this.worker = worker;
}

queue.prototype.put = function(klass, data, options, cb, eb) {
	this.client._put.call([this.name], [
		options.jid || uuid.v1().replace(/-/g, ''),
		klass,
		JSON.stringify(data),
		(new Date().getTime() / 1000),
		options.delay || 0,
		'priority', options.priority || 0,
		'tags', JSON.stringify(options.tags || []),
		'retries', options.retries || 5,
		'depends', JSON.stringify(options.depends || [])
	], cb, eb)
}

queue.prototype.pop = function(count, cb, eb) {
	if (typeof(count) == 'function') {
		eb = cb; cb = count;
		this.client._pop.call([this.name], [this.worker, 1, (new Date().getTime() / 1000)], function(r) {
			cb(new job(JSON.parse(r)));
		}, eb);
	} else {
		this.client._pop.call([this.name], [this.worker, 1, (new Date().getTime() / 1000)], function(r) {
			var results = JSON.parse(r);
			for (var index in results) {
				results[index] = new job(results[index]);
			}
			cb(results);
		}, eb);
	}
}

queue.prototype.peek = function(count, cb, eb) {
	if (typeof(count) == 'function') {
		eb = cb; cb = count;
		this.client._peek.call([this.name], [1, (new Date().getTime() / 1000)], function(r) {
			cb(new job(JSON.parse(r)));
		}, eb);
	} else {
		this.client._peek.call([this.name], [1, (new Date().getTime() / 1000)], function(r) {
			var results = JSON.parse(r);
			for (var index in results) {
				results[index] = new job(results[index]);
			}
			cb(results);
		}, eb);
	}
}

queue.prototype.stats = function(date, cb, eb) {
	if (typeof(date) == 'function') {
		eb = cb; cb = date; date = (new Date().getTime() / 1000);
	}
	this.client._stats.call([], [this.name, date], function(r) {
		cb(JSON.parse(r));
	}, eb);
}

queue.prototype.running = function(offset, count, cb, eb) {
	if (typeof(offset) == 'function') {
		eb = count; cb = offset; offset = 0; count = 25;
	} else if (typeof(count) == 'function') {
		eb = cb; cb = count; count = 25;
	}
	this.client._jobs([], ['running', (new Date().getTime() / 1000), this.name, offset, count], cb, eb);
}

queue.prototype.stalled = function(offset, count, cb, eb) {
	if (typeof(offset) == 'function') {
		eb = count; cb = offset; offset = 0; count = 25;
	} else if (typeof(count) == 'function') {
		eb = cb; cb = count; count = 25;
	}
	this.client._jobs([], ['stalled', (new Date().getTime() / 1000), this.name, offset, count], cb, eb);
}

queue.prototype.scheduled = function(offset, count, cb, eb) {
	if (typeof(offset) == 'function') {
		eb = count; cb = offset; offset = 0; count = 25;
	} else if (typeof(count) == 'function') {
		eb = cb; cb = count; count = 25;
	}
	this.client._jobs([], ['scheduled', (new Date().getTime() / 1000), this.name, offset, count], cb, eb);
}

queue.prototype.depends = function(offset, count, cb, eb) {
	if (typeof(offset) == 'function') {
		eb = count; cb = offset; offset = 0; count = 25;
	} else if (typeof(count) == 'function') {
		eb = cb; cb = count; count = 25;
	}
	this.client._jobs([], ['depends', (new Date().getTime() / 1000), this.name, offset, count], cb, eb);
}

queue.prototype.length = function(cb, eb) {
	this.client.redis.multi()
		.zcard('ql:q:' + this.name + '-locks')
		.zcard('ql:q:' + this.name + '-work')
		.zcard('ql:q:' + this.name + '-scheduled')
		.exec(function(err, r) {
			err ? (eb || console.warn)(err) : (cb || function(){})(r[0] + r[1] + r[2]);
		});
}

exports.queue = queue;<commit_msg>fixed bad handling of empty/nonexistent queues; attempted to fix peek/pop using 'count' argument but this is untested<commit_after>var uuid = require('node-uuid');
var job  = require('./job').job;

function queue(name, client, worker) {
	this.name   = name;
	this.client = client;
	this.worker = worker;
}

queue.prototype.put = function(klass, data, options, cb, eb) {
	this.client._put.call([this.name], [
		options.jid || uuid.v1().replace(/-/g, ''),
		klass,
		JSON.stringify(data),
		(new Date().getTime() / 1000),
		options.delay || 0,
		'priority', options.priority || 0,
		'tags', JSON.stringify(options.tags || []),
		'retries', options.retries || 5,
		'depends', JSON.stringify(options.depends || [])
	], cb, eb)
}

queue.prototype.pop = function(count, cb, eb) {
	if (typeof(count) == 'function') {
		eb = cb; cb = count;
		var selfclient = this.client;
		this.client._pop.call([this.name], [this.worker, 1, (new Date().getTime() / 1000)], function(r) {
			if (r.length) {
				cb(new job(selfclient, JSON.parse(r)));
			} else {
				cb({});
			}
		}, eb);
	} else {
		this.client._pop.call([this.name], [this.worker, count, (new Date().getTime() / 1000)], function(r) {
			var results = JSON.parse(r);
			for (var index in results) {
				results[index] = new job(selfclient, results[index]);
			}
			cb(results);
		}, eb);
	}
}

queue.prototype.peek = function(count, cb, eb) {
	if (typeof(count) == 'function') {
		eb = cb; cb = count;
		var selfclient = this.client;
		this.client._peek.call([this.name], [1, (new Date().getTime() / 1000)], function(r) {
			if (r.length) {
				cb(new job(selfclient, JSON.parse(r)));
			} else {
				cb({});
			}
		}, eb);
	} else {
		this.client._peek.call([this.name], [count, (new Date().getTime() / 1000)], function(r) {
			var results = JSON.parse(r);
			for (var index in results) {
				results[index] = new job(selfclient, results[index]);
			}
			cb(results);
		}, eb);
	}
}

queue.prototype.stats = function(date, cb, eb) {
	if (typeof(date) == 'function') {
		eb = cb; cb = date; date = (new Date().getTime() / 1000);
	}
	this.client._stats.call([], [this.name, date], function(r) {
		cb(JSON.parse(r));
	}, eb);
}

queue.prototype.running = function(offset, count, cb, eb) {
	if (typeof(offset) == 'function') {
		eb = count; cb = offset; offset = 0; count = 25;
	} else if (typeof(count) == 'function') {
		eb = cb; cb = count; count = 25;
	}
	this.client._jobs([], ['running', (new Date().getTime() / 1000), this.name, offset, count], cb, eb);
}

queue.prototype.stalled = function(offset, count, cb, eb) {
	if (typeof(offset) == 'function') {
		eb = count; cb = offset; offset = 0; count = 25;
	} else if (typeof(count) == 'function') {
		eb = cb; cb = count; count = 25;
	}
	this.client._jobs([], ['stalled', (new Date().getTime() / 1000), this.name, offset, count], cb, eb);
}

queue.prototype.scheduled = function(offset, count, cb, eb) {
	if (typeof(offset) == 'function') {
		eb = count; cb = offset; offset = 0; count = 25;
	} else if (typeof(count) == 'function') {
		eb = cb; cb = count; count = 25;
	}
	this.client._jobs([], ['scheduled', (new Date().getTime() / 1000), this.name, offset, count], cb, eb);
}

queue.prototype.depends = function(offset, count, cb, eb) {
	if (typeof(offset) == 'function') {
		eb = count; cb = offset; offset = 0; count = 25;
	} else if (typeof(count) == 'function') {
		eb = cb; cb = count; count = 25;
	}
	this.client._jobs([], ['depends', (new Date().getTime() / 1000), this.name, offset, count], cb, eb);
}

queue.prototype.length = function(cb, eb) {
	this.client.redis.multi()
		.zcard('ql:q:' + this.name + '-locks')
		.zcard('ql:q:' + this.name + '-work')
		.zcard('ql:q:' + this.name + '-scheduled')
		.exec(function(err, r) {
			err ? (eb || console.warn)(err) : (cb || function(){})(r[0] + r[1] + r[2]);
		});
}

exports.queue = queue;<|endoftext|>