<commit_before>var util = require('util');
var events = require('events');
var h2connection = require('./connection');
var h2frame = require('./frame');

var HTTP2_PREFACE = new Buffer('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n', 'ascii');

var handleFrame = function (stream, frame) {
    var handler = frameHandlers[frame.type];
    if (handler) {
        handler.call(this, stream, frame);
    } else if (handler === null) {
        // ignore the frame
    } else {
        console.error('no handler: %d', frame.type);
    }
};

var handleDataFrame = function (stream, frame) {
    var data = frame.getData();
    stream.emit('data', data);
};

var handleHeadersFrame = function (stream, frames) {
    var i, headerBlocks = [];
    for (i = 0; i < frames.length; i++) {
        headerBlocks.push(frames[i].getBlock());
    }
    var headers = this.connection.hpack.decode(Buffer.concat(headerBlocks));
    stream.emit('header', headers);
};

var handleSettingsFrame = function (stream, frame) {
    var p, i, params, nParam, ackFrame;
    if (frame.flags & h2frame.Http2SettingsFrame.FLAG_ACK) {
        params = this._settingQueue.shift();
        if (params) {
            nParam = params.length;
            for (i = 0; i < nParam; i++) {
                p = params[i];
                this.connection.setRemoteSetting(p.id, p.value);
            }
        }
    } else {
        nParam = frame.getParamCount();
        for (i = 0; i < nParam; i++) {
            p = frame.getParamByIndex(i);
            this.connection.setLocalSetting(p.id, p.value);
        }
        if (this.config['http2.auto_ack']) {
            ackFrame = new h2frame.Http2SettingsFrame();
            ackFrame.flags |= h2frame.Http2SettingsFrame.FLAG_ACK;
            this.connection.streams[0].send(ackFrame);
        }
    }
};

var handlePushPromiseFrame = function (stream, frame) {
    var self = this;
    var id = stream.connection.newStream(frame.promisedStreamId);
    var newStream = this.connection.streams[id];
    newStream.on('close', function () {});
    newStream.on('header', function () {});
    newStream.on('data', function (data) {
        if (data.length) {
            var frame = new h2frame.Http2WindowUpdateFrame();
            frame.windowSizeIncrement = data.length;
            this.send(frame);
            self.connection.streams[0].send(frame);
        }
    });
};

var handlePingFrame = function (stream, frame) {
    if (!frame.flags & h2frame.Http2PingFrame.FLAG_ACK) {
        var ackFrame = new h2frame.Http2PingFrame();
        ackFrame.setPayload(frame.getPayload());
        ackFrame.flags |= h2frame.Http2PingFrame.FLAG_ACK;
        this.connection.streams[0].send(ackFrame);
    }
};

var handleGoawayFrame = function (stream, frame) {
    this.connection.close();
};

var frameHandlers = [
    handleDataFrame,
    null,
    void(0),
    void(0),
    handleSettingsFrame,
    handlePushPromiseFrame,
    handlePingFrame,
    handleGoawayFrame,
    void(0),
    null,
    void(0),
    void(0),
    void(0),
];

var H2 = function (config) {
    this.config = {
        'http2.auto_preface': 1,
        'http2.auto_ack': 1,
        'http2.use_padding': 0,
        'log.state_change': 0,
        'log.frame_sent': 0,
        'log.frame_received': 0,
        'hpack.use_huffman': 1
    };

    var self = this;
    if (config) {
        Object.keys(config).forEach(function (k) {
            self.config[k] = config[k];
        });
    }
};
util.inherits(H2, events.EventEmitter);

H2.prototype.connect = function (hostname, port, options, callback) {
    var self = this;
    this.connection = new h2connection.Connection(hostname, port, options);
    this.connection.hpack.useHuffman(self.config['hpack.use_huffman']);
    this.connection.on('connect', function (stream) {
        if (options['useUpgrade']) {
            // TODO useUpgrade
        } else {
            self.connection.newStream();
            if (self.config['http2.auto_preface']) {
                self.connection.send(HTTP2_PREFACE);
                self.connection.streams[0].send(new h2frame.Http2SettingsFrame());
            }
        }
        callback(stream);
    });
    this.connection.on('close', function () {
        self.connection = void(0);
    });
    this.connection.on('error', function () {
        callback(null, true);
    });
    this.connection.on('newStream', function (stream) {
        stream.on('frame', function (frame) {
            if (self.config['log.frame_received']) {
                console.log('RECV[%d]: ' + frame, this.id);
            }
            handleFrame.call(self, this, frame);
        });
        stream.on('headerFrames', function (frames) {
            handleHeadersFrame.call(self, this, frames);
        });
        stream.on('send', function (frame) {
            if (self.config['log.frame_sent']) {
                console.log("SEND[%d]: " + frame, this.id);
            }
        });
        stream.on('stateChange', function (oldState, newState) {
            if (self.config['log.state_change']) {
                console.log('STATE CHANGE[%d]: %s -> %s', this.id, oldState, newState);
            }
        });
    });
    this.connection.on('frame', function (data) {
        var frame = h2frame.Http2FrameFactory.createFrame(data);
        var stream = self.connection.streams[frame.streamId];
        if (!stream) {
            console.log('Unexpected streamId: ' + frame.streamId);
        }
        stream.consumeFrame(frame);
    });
    this._settingQueue = [];
};

H2.prototype.close = function (callback) {
    if (this.connection) {
        this.connection.close(callback);
    } else {
        if (callback) {
            callback();
        }
    }
};

H2.prototype.send = function (frame, streamId, callback) {
    if (!this.connection) {
        console.error('Not connected yet.');
        arguments[arguments.length - 1]();
        return;
    }
    if (arguments[0] instanceof h2frame.Http2Frame) {
        if (typeof arguments[1] === 'number') {
            this.connection.newStream(streamId);
            this.connection.streams[streamId].send(frame, arguments[2]);
        } else {
            this.connection.streams[0].send(frame, arguments[1]);
        }
    } else {
        this.connection.send(frame, callback);
    }
};

H2.prototype.request = function (/* authority, path, method, data, callbacks */) {
    var self = this, authority, path, method, data, callbacks, pos, i, f;
    pos = 0;
    if (arguments[0][0] === '/') {
        authority = self.connection ? self.connection.hostname : '';
    } else {
        authority = arguments[pos++];
    }
    path      = arguments[pos++];
    method    = arguments[pos++];
    if (typeof arguments[pos] === 'string') {
        data = new Buffer(arguments[pos++]);
        callbacks = arguments[pos];
    } else {
        callbacks = arguments[pos];
    }
    if (!self.connection) {
        console.error('Not connected yet.');
        callbacks.error();
        return;
    }
    var headers = [
        [':method', method],
        [':scheme', self.connection.secure ? 'https' : 'http'],
        [':authority', authority],
        [':path', path],
        ['user-agent', 'h2cli/0.17.1']
    ];
    if (data) {
        headers.push(['content-length', '' + data.length]);
    }
    var id = this.connection.newStream();
    var stream = this.connection.streams[id];
    stream.on('close', callbacks.close);
    stream.on('header', callbacks.header);
    stream.on('data', function (data) {
        callbacks.data(data);
        if (data.length) {
            var frame = new h2frame.Http2WindowUpdateFrame();
            frame.windowSizeIncrement = data.length;
            this.send(frame);
            self.connection.streams[0].send(frame);
        }
    });
    var headersFrames = h2frame.Http2FrameFactory.createRequestFrames(this.connection.hpack, headers, this.config['http2.use_padding']);
    if (data) {
        stream.send(headersFrames);
        var dataFrames = h2frame.Http2FrameFactory.createDataFrames(data, this.config['http2.use_padding']);
        dataFrames[dataFrames.length - 1].flags |= h2frame.Http2DataFrame.FLAG_END_STREAM;
        stream.send(dataFrames);
    } else {
        headersFrames[headersFrames.length - 1].flags |= h2frame.Http2HeadersFrame.FLAG_END_STREAM;
        stream.send(headersFrames);
    }
};

H2.prototype.setSetting = function (name, value, callback) {
    var frame = new h2frame.Http2SettingsFrame();
    var paramId = h2frame.Http2SettingsFrame['PARAM_SETTINGS_' + name];
    if (paramId) {
        frame.setParam(paramId, value);
        this._settingQueue.push([{'id': paramId, 'value': value}]);
        this.connection.streams[0].send(frame);
    }
    callback();
};

H2.prototype.setConfig = function (name, value) {
    this.config[name] = parseInt(value, 10);
    if (name === 'hpack.use_huffman') {
        if (this.connection) {
            this.connection.hpack.useHuffman(this.config['hpack.use_huffman']);
        }
    }
};

module.exports = {
    'HTTP2_PREFACE': HTTP2_PREFACE,
    'frame': h2frame,
    'createClient': function (config) {
        return new H2(config);
    },
    'registerFrameHandler': function (type, handler) {
        frameHandlers[type] = handler;
    },
};
<commit_msg>append port number to authority<commit_after>var util = require('util');
var events = require('events');
var h2connection = require('./connection');
var h2frame = require('./frame');

var HTTP2_PREFACE = new Buffer('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n', 'ascii');

var handleFrame = function (stream, frame) {
    var handler = frameHandlers[frame.type];
    if (handler) {
        handler.call(this, stream, frame);
    } else if (handler === null) {
        // ignore the frame
    } else {
        console.error('no handler: %d', frame.type);
    }
};

var handleDataFrame = function (stream, frame) {
    var data = frame.getData();
    stream.emit('data', data);
};

var handleHeadersFrame = function (stream, frames) {
    var i, headerBlocks = [];
    for (i = 0; i < frames.length; i++) {
        headerBlocks.push(frames[i].getBlock());
    }
    var headers = this.connection.hpack.decode(Buffer.concat(headerBlocks));
    stream.emit('header', headers);
};

var handleSettingsFrame = function (stream, frame) {
    var p, i, params, nParam, ackFrame;
    if (frame.flags & h2frame.Http2SettingsFrame.FLAG_ACK) {
        params = this._settingQueue.shift();
        if (params) {
            nParam = params.length;
            for (i = 0; i < nParam; i++) {
                p = params[i];
                this.connection.setRemoteSetting(p.id, p.value);
            }
        }
    } else {
        nParam = frame.getParamCount();
        for (i = 0; i < nParam; i++) {
            p = frame.getParamByIndex(i);
            this.connection.setLocalSetting(p.id, p.value);
        }
        if (this.config['http2.auto_ack']) {
            ackFrame = new h2frame.Http2SettingsFrame();
            ackFrame.flags |= h2frame.Http2SettingsFrame.FLAG_ACK;
            this.connection.streams[0].send(ackFrame);
        }
    }
};

var handlePushPromiseFrame = function (stream, frame) {
    var self = this;
    var id = stream.connection.newStream(frame.promisedStreamId);
    var newStream = this.connection.streams[id];
    newStream.on('close', function () {});
    newStream.on('header', function () {});
    newStream.on('data', function (data) {
        if (data.length) {
            var frame = new h2frame.Http2WindowUpdateFrame();
            frame.windowSizeIncrement = data.length;
            this.send(frame);
            self.connection.streams[0].send(frame);
        }
    });
};

var handlePingFrame = function (stream, frame) {
    if (!frame.flags & h2frame.Http2PingFrame.FLAG_ACK) {
        var ackFrame = new h2frame.Http2PingFrame();
        ackFrame.setPayload(frame.getPayload());
        ackFrame.flags |= h2frame.Http2PingFrame.FLAG_ACK;
        this.connection.streams[0].send(ackFrame);
    }
};

var handleGoawayFrame = function (stream, frame) {
    this.connection.close();
};

var frameHandlers = [
    handleDataFrame,
    null,
    void(0),
    void(0),
    handleSettingsFrame,
    handlePushPromiseFrame,
    handlePingFrame,
    handleGoawayFrame,
    void(0),
    null,
    void(0),
    void(0),
    void(0),
];

var H2 = function (config) {
    this.config = {
        'http2.auto_preface': 1,
        'http2.auto_ack': 1,
        'http2.use_padding': 0,
        'log.state_change': 0,
        'log.frame_sent': 0,
        'log.frame_received': 0,
        'hpack.use_huffman': 1
    };

    var self = this;
    if (config) {
        Object.keys(config).forEach(function (k) {
            self.config[k] = config[k];
        });
    }
};
util.inherits(H2, events.EventEmitter);

H2.prototype.connect = function (hostname, port, options, callback) {
    var self = this;
    this.connection = new h2connection.Connection(hostname, port, options);
    this.connection.hpack.useHuffman(self.config['hpack.use_huffman']);
    this.connection.on('connect', function (stream) {
        if (options['useUpgrade']) {
            // TODO useUpgrade
        } else {
            self.connection.newStream();
            if (self.config['http2.auto_preface']) {
                self.connection.send(HTTP2_PREFACE);
                self.connection.streams[0].send(new h2frame.Http2SettingsFrame());
            }
        }
        callback(stream);
    });
    this.connection.on('close', function () {
        self.connection = void(0);
    });
    this.connection.on('error', function () {
        callback(null, true);
    });
    this.connection.on('newStream', function (stream) {
        stream.on('frame', function (frame) {
            if (self.config['log.frame_received']) {
                console.log('RECV[%d]: ' + frame, this.id);
            }
            handleFrame.call(self, this, frame);
        });
        stream.on('headerFrames', function (frames) {
            handleHeadersFrame.call(self, this, frames);
        });
        stream.on('send', function (frame) {
            if (self.config['log.frame_sent']) {
                console.log("SEND[%d]: " + frame, this.id);
            }
        });
        stream.on('stateChange', function (oldState, newState) {
            if (self.config['log.state_change']) {
                console.log('STATE CHANGE[%d]: %s -> %s', this.id, oldState, newState);
            }
        });
    });
    this.connection.on('frame', function (data) {
        var frame = h2frame.Http2FrameFactory.createFrame(data);
        var stream = self.connection.streams[frame.streamId];
        if (!stream) {
            console.log('Unexpected streamId: ' + frame.streamId);
        }
        stream.consumeFrame(frame);
    });
    this._settingQueue = [];
};

H2.prototype.close = function (callback) {
    if (this.connection) {
        this.connection.close(callback);
    } else {
        if (callback) {
            callback();
        }
    }
};

H2.prototype.send = function (frame, streamId, callback) {
    if (!this.connection) {
        console.error('Not connected yet.');
        arguments[arguments.length - 1]();
        return;
    }
    if (arguments[0] instanceof h2frame.Http2Frame) {
        if (typeof arguments[1] === 'number') {
            this.connection.newStream(streamId);
            this.connection.streams[streamId].send(frame, arguments[2]);
        } else {
            this.connection.streams[0].send(frame, arguments[1]);
        }
    } else {
        this.connection.send(frame, callback);
    }
};

H2.prototype.request = function (/* authority, path, method, data, callbacks */) {
    var self = this, authority, path, method, data, callbacks, pos, i, f;
    pos = 0;
    if (arguments[0][0] === '/') {
        authority = self.connection ? self.connection.hostname : '';
    } else {
        authority = arguments[pos++];
    }
    if (self.connection &&
        ((self.connection.port !== 443 || !self.connection.secure) &&
        (self.connection.port !== 80 || self.connection.secure))) {
            authority += ':' + self.connection.port;
    }
    path      = arguments[pos++];
    method    = arguments[pos++];
    if (typeof arguments[pos] === 'string') {
        data = new Buffer(arguments[pos++]);
        callbacks = arguments[pos];
    } else {
        callbacks = arguments[pos];
    }
    if (!self.connection) {
        console.error('Not connected yet.');
        callbacks.error();
        return;
    }
    var headers = [
        [':method', method],
        [':scheme', self.connection.secure ? 'https' : 'http'],
        [':authority', authority],
        [':path', path],
        ['user-agent', 'h2cli/0.17.1']
    ];
    if (data) {
        headers.push(['content-length', '' + data.length]);
    }
    var id = this.connection.newStream();
    var stream = this.connection.streams[id];
    stream.on('close', callbacks.close);
    stream.on('header', callbacks.header);
    stream.on('data', function (data) {
        callbacks.data(data);
        if (data.length) {
            var frame = new h2frame.Http2WindowUpdateFrame();
            frame.windowSizeIncrement = data.length;
            this.send(frame);
            self.connection.streams[0].send(frame);
        }
    });
    var headersFrames = h2frame.Http2FrameFactory.createRequestFrames(this.connection.hpack, headers, this.config['http2.use_padding']);
    if (data) {
        stream.send(headersFrames);
        var dataFrames = h2frame.Http2FrameFactory.createDataFrames(data, this.config['http2.use_padding']);
        dataFrames[dataFrames.length - 1].flags |= h2frame.Http2DataFrame.FLAG_END_STREAM;
        stream.send(dataFrames);
    } else {
        headersFrames[headersFrames.length - 1].flags |= h2frame.Http2HeadersFrame.FLAG_END_STREAM;
        stream.send(headersFrames);
    }
};

H2.prototype.setSetting = function (name, value, callback) {
    var frame = new h2frame.Http2SettingsFrame();
    var paramId = h2frame.Http2SettingsFrame['PARAM_SETTINGS_' + name];
    if (paramId) {
        frame.setParam(paramId, value);
        this._settingQueue.push([{'id': paramId, 'value': value}]);
        this.connection.streams[0].send(frame);
    }
    callback();
};

H2.prototype.setConfig = function (name, value) {
    this.config[name] = parseInt(value, 10);
    if (name === 'hpack.use_huffman') {
        if (this.connection) {
            this.connection.hpack.useHuffman(this.config['hpack.use_huffman']);
        }
    }
};

module.exports = {
    'HTTP2_PREFACE': HTTP2_PREFACE,
    'frame': h2frame,
    'createClient': function (config) {
        return new H2(config);
    },
    'registerFrameHandler': function (type, handler) {
        frameHandlers[type] = handler;
    },
};
<|endoftext|>


<commit_before>console.log("PLUGIN: MEETUP loaded");

var color = require('irc-colors');
var request = require('request');

var api_key = process.env.IRCBOT_MEETUP_API_KEY || '';

var url = 'https://api.meetup.com/2/events?key=' +
    api_key +
    '&group_urlname=stuttgartjs&sign=true';

!!api_key || console.log('WARNING: IRCBOT_MEETUP_API_KEY missing - set -x IRCBOT_MEETUP_API_KEY "CHANGE_IT"');

module.exports = function (client, config) {

    client.addListener('message', function (nick, to, message, raw) {

        var cmdline = message.split(' ').filter(function (el) {
            return el.length > 0;
        });

        if (cmdline[0].toLowerCase() !== '!meetup' ||
            nick.toLowerCase() === config.irc.nickname.toLowerCase() ||
            (config.plugins.rss.channels.length !== 0 &&
                to[0] === '#' &&
                !config.plugins.rss.channels.some(function (el) {
                    return el == to;
                }))) {

            console.log('MEETUP: command canceld (2)');
            return;
        }

        if (to.toLowerCase() === config.irc.nickname.toLowerCase()) {

            // bot got the message
            to = nick;
        }

        console.log("MEETUP CMD:", nick, to, message, raw);

        request(url, function (error, response, body) {

            //console.log("ERROR:", error);

            if (!error && response.statusCode == 200) {

                var item = JSON.parse(body).results[0];
                //console.log(item);

                var d = (new Date(item.time) + '').split(' ');
                var date = [d[2], d[1], d[3], d[4]].join(' ');

                sendMessage(client, to,
                    color.green(date) + ' ' +
                    color.red(item.name) + ' ' +
                    item.yes_rsvp_count + ' ' +
                    'Teilnehmer' + ' ' +
                    item.event_url
                );

            }
        });
    });

}

function sendMessage(client, to, message) {

    client.say(to, color.green('MEETUP: ') + color.red(message));
    console.log('MEETUP: sendMessage:', to, message);
}
<commit_msg>cleanup meetup plugin<commit_after>console.log("PLUGIN: MEETUP loaded");

var utils = require('../../lib/utils');

var color = require('irc-colors');
var request = require('request');

var api_key = process.env.IRCBOT_MEETUP_API_KEY || '';

var url = 'https://api.meetup.com/2/events?key=' +
    api_key +
    '&group_urlname=stuttgartjs&sign=true';

!!api_key || console.log('WARNING: IRCBOT_MEETUP_API_KEY missing - set -x IRCBOT_MEETUP_API_KEY "CHANGE_IT"');

function init(client, config) {

    client.addListener('message', function (nick, to, message, raw) {

        var cmdline = utils.filterCommands(message);

        if (!utils.isCmd('!meetup', cmdline)) {
            return;
        }
        if (utils.isBotMessage(to, config)) {
            to = nick;
        }

        !!process.env.IRCBOT_DEBUG && console.log("MEETUP CMD:", nick, to, message, raw);

        request(url, function (error, response, body) {

            !!process.env.IRCBOT_DEBUG && console.log("ERROR:", error);

            if (!error && response.statusCode == 200) {

                var item = JSON.parse(body).results[0];

                var d = (new Date(item.time) + '').split(' ');
                var date = [d[2], d[1], d[3], d[4]].join(' ');

                sendMessage(client, to,
                    color.green(date) + ' ' +
                    color.red(item.name) + ' ' +
                    item.yes_rsvp_count + ' ' +
                    'Teilnehmer' + ' ' +
                    item.event_url
                );

            }
        });
    });

}

function sendMessage(client, to, message) {

    client.say(to, color.green('MEETUP: ') + color.red(message));
    console.log('MEETUP: sendMessage:', to, message);
}

function help() {
    return (
        color.green('HELP: meetup')
    );
}

module.exports = {
    init: init,
    help: help
}
<|endoftext|>


<commit_before>
/**
 * The 'id' function is essentially the "base class" for all Objective-C
 * objects that get passed around JS-land.
 */

/*!
 * Module exports.
 */

exports.wrap = wrap

/*!
 * Module dependencies.
 */

var debug = require('debug')('NodObjC')
  , proto = exports.proto = Object.create(Function.prototype)
  , core  = require('./core')
  , Class = require('./class')
  , types = require('./types')
  , SEL   = require('./sel')
  , exception = require('./exception')
  , assert = require('assert')

/*!
 * An arbitrary "key" pointer we use for storing the JS-wrap instance reference
 * into the ObjC object's internal weak map via `objc_getAssociatedObject()`.
 */

var KEY = new core.Pointer(1)

/**
 * Wraps up a pointer that is expected to be a compatible Objective-C
 * object that can recieve messages. This function returns a cached version of the
 * wrapped function after the first time it is invoked on a given Pointer, using
 * Objective-C's internal association map for objects.
 *
 * @api private
 */

function wrap (pointer) {
  debug('id#wrap(%d)', pointer.address)
  var rtn = null
    , p = core.objc_getAssociatedObject(pointer, KEY)
  if (p.isNull()) {
    rtn = createFunctionWrapper(pointer)
    // Store the wrapped instance internally
    var ref = new core.Pointer(core.TYPE_SIZE_MAP.Object)
    // don't call free() automatically when ref gets GC'd
    // TODO: we're gonna have to free this pointer someday!
    // XXX: use node-weak to get a callback when the wrapper is GC'd
    ref.free = false
    ref.putObject(rtn)
    core.objc_setAssociatedObject(pointer, KEY, ref, 0)
  } else {
    debug('returning cached associated instance')
    rtn = p.getObject()
  }
  //assert.equal(rtn.pointer.address, pointer.address)
  return rtn
}

/*!
 * Internal function that essentially "creates" a new Function instance wrapping
 * the given pointer. The function's implementation is the "id()" function below,
 *
 * XXX: Maybe use `Function.create()` from my `create` module here (benchmark)???
 *
 * @api private
 */

function createFunctionWrapper (pointer) {
  debug('createFunctionWrapper(%d)', pointer.address)

  // This 'id' function is syntax sugar around the msgSend function attached to
  // it. 'msgSend' is expecting the selector first, an Array of args second, so
  // this function just massages it into place and returns the result.
  function id () {
    var args = []
      , sel
    if (arguments.length === 1) {
      var arg = arguments[0]
      if (arg.constructor.name == 'String') {
        sel = arg
      } else {
        sel = []
        Object.keys(arg).forEach(function (s) {
          sel.push(s)
          args.push(arg[s])
        })
        sel.push('')
        sel = sel.join(':')
      }
    } else {
      sel = []
      var len = arguments.length
      for (var i=0; i<len; i+=2) {
        sel.push(arguments[i])
        args.push(arguments[i+1])
      }
      sel.push('')
      sel = sel.join(':')
    }
    return id.msgSend(sel, args)
  }

  // Set the "type" on the pointer. This is used by 'ref()' and 'unref()'.
  pointer._type = '@'
  // Save a reference to the pointer for use by the prototype functions
  id.pointer = pointer
  // Morph into a MUTANT FUNCTION FREAK!!1!
  id.__proto__ = proto
  return id
}

/**
 * A very important function that *does the message sending* between
 * Objective-C objects. When you do `array('addObject', anObject)`, this
 * `msgSend` function is the one that finally gets called to do the dirty work.
 *
 * This function accepts a String selector as the first argument, and an Array
 * of (wrapped) values that get passed to the the message. This function takes
 * care of unwrapping the passed in arguments and wrapping up the result value,
 * if necessary.
 */

proto.msgSend = function msgSend (sel, args) {
  debug('sending message:', sel, args)
  var types = this._getTypes(sel, args)
    , argTypes = types[1]
    , msgSendFunc = core.get_objc_msgSend(types)
    , unwrappedArgs = core.unwrapValues([this, sel].concat(args), argTypes)
    , rtn
  try {
    rtn = msgSendFunc.apply(null, unwrappedArgs)
  } catch (e) {
    throw exception.wrap(e)
  }
  // Process the return value into a wrapped value if needed
  return core.wrapValue(rtn, types[0])
}

/**
 * Accepts a SEL and queries the current object for the return type and
 * argument types for the given selector. If current object does not implment
 * that selector, then check the superclass, and repeat recursively until
 * a subclass that responds to the selector is found, or until the base class
 * is found.
 *
 * TODO: Just merge this logic with `msgSend()`? It's not used anywhere else
 *
 * @api private
 */

proto._getTypes = function getTypes (sel, args) {
  var c = this.getClass()
    , t = c._getTypesClass(sel, this.isClass)
  if (!t) {
    // Unknown selector being send to object. This *may* still be valid, we
    // assume all args are type 'id' and return is 'id'.
    debug('unknown selector being sent:', sel)
    t = [ '@', [ '@', ':', ].concat(args.map(function () { return '@' })) ]
  }
  return t
}

/**
 * Retrieves the wrapped Class instance for this object.
 */

proto.getClass = function getClass () {
  return Class.wrap(core.object_getClass(this.pointer))
}

/**
 * Calls 'object_getClassName()' on this object.
 */

proto.getClassName = function getClassName () {
  return core.object_getClassName(this.pointer)
}

/**
 * Dynamically changes the object's Class.
 */

proto.setClass = function setClass (newClass) {
  return Class.wrap(core.object_setClass(this.pointer, newClass.pointer))
}

/**
 * Walks up the inheritance chain and returns an Array of Strings of
 * superclasses.
 */

proto.ancestors = function ancestors () {
  var rtn = []
    , c = this.getClass()
  while (c) {
    rtn.push(c.getName())
    c = c.getSuperclass()
  }
  return rtn
}

/**
 * Getter/setter function for instance variables (ivars) of the object,
 * If just a name is passed in, then this function gets the ivar current value.
 * If a name and a new value are passed in, then this function sets the ivar.
 */

proto.ivar = function ivar (name, value) {
  // TODO: Add support for passing in a wrapped Ivar instance as the `name`
  if (arguments.length > 1) {
    // setter
    debug('setting ivar:', name, value)
    var ivar = this.isClass
             ? this.getClassVariable(name)
             : this.getClass().getInstanceVariable(name)
      , unwrapped = core.unwrapValue(value, ivar.getTypeEncoding())
    return core.object_setIvar(this.pointer, ivar.pointer, unwrapped)
  } else {
    // getter
    debug('getting ivar:', name)
    var ptr = new core.Pointer(core.TYPE_SIZE_MAP.pointer)
      , ivar = core.object_getInstanceVariable(this.pointer, name, ptr)
    return core.wrapValue(ptr.getPointer(), core.ivar_getTypeEncoding(ivar))
  }
}

/**
 * Returns an Array of Strings of the names of the ivars that the current object
 * contains. This function can iterate through the object's superclasses
 * recursively, if you specify a 'maxDepth' argument.
 */

proto.ivars = function ivars (maxDepth, sort) {
  var rtn = []
    , c = this.getClass()
    , md = maxDepth || 1
    , depth = 0
  while (c && depth++ < md) {
    var is = c.getInstanceVariables()
      , i = is.length
    while (i--) {
      if (!~rtn.indexOf(is[i])) rtn.push(is[i])
    }
    c = c.getSuperclass()
  }
  return sort === false ? rtn : rtn.sort()
}

/**
 * Returns an Array of Strings of the names of methods that the current object
 * will respond to. This function can iterate through the object's superclasses
 * recursively, if you specify a 'maxDepth' number argument.
 */

proto.methods = function methods (maxDepth, sort) {
  var rtn = []
    , c = this.getClass()
    , md = maxDepth || 1
    , depth = 0
  while (c && depth++ < md) {
    var ms = c.getInstanceMethods()
      , i = ms.length
    while (i--) {
      if (!~rtn.indexOf(ms[i])) rtn.push(ms[i])
    }
    c = c.getSuperclass()
  }
  return sort === false ? rtn : rtn.sort()
}

/**
 * Returns a **node-ffi** pointer pointing to this object. This is a convenience
 * function for methods that take pointers to objects (i.e. `NSError**`).
 *
 * @return {Pointer} A pointer to this object.
 */

proto.ref = function ref () {
  debug('id#ref()')
  var ptr = this.pointer.ref()
  return ptr
}

/**
 * The overidden `toString()` function proxies up to the real Objective-C object's
 * `description` method. In Objective-C, this is equivalent to:
 *
 *     [[id description] UTF8String]
 */

proto.toString = function toString () {
  return this('description')('UTF8String')
}

/*!
 * Custom inspect() function for `util.inspect()`.
 */

proto.inspect = function inspect () {
  return this.toString()
}
<commit_msg>A micro-optimization on the id function.<commit_after>
/**
 * The 'id' function is essentially the "base class" for all Objective-C
 * objects that get passed around JS-land.
 */

/*!
 * Module exports.
 */

exports.wrap = wrap

/*!
 * Module dependencies.
 */

var debug = require('debug')('NodObjC')
  , proto = exports.proto = Object.create(Function.prototype)
  , core  = require('./core')
  , Class = require('./class')
  , types = require('./types')
  , SEL   = require('./sel')
  , exception = require('./exception')
  , assert = require('assert')

/*!
 * An arbitrary "key" pointer we use for storing the JS-wrap instance reference
 * into the ObjC object's internal weak map via `objc_getAssociatedObject()`.
 */

var KEY = new core.Pointer(1)

/**
 * Wraps up a pointer that is expected to be a compatible Objective-C
 * object that can recieve messages. This function returns a cached version of the
 * wrapped function after the first time it is invoked on a given Pointer, using
 * Objective-C's internal association map for objects.
 *
 * @api private
 */

function wrap (pointer) {
  debug('id#wrap(%d)', pointer.address)
  var rtn = null
    , p = core.objc_getAssociatedObject(pointer, KEY)
  if (p.isNull()) {
    rtn = createFunctionWrapper(pointer)
    // Store the wrapped instance internally
    var ref = new core.Pointer(core.TYPE_SIZE_MAP.Object)
    // don't call free() automatically when ref gets GC'd
    // TODO: we're gonna have to free this pointer someday!
    // XXX: use node-weak to get a callback when the wrapper is GC'd
    ref.free = false
    ref.putObject(rtn)
    core.objc_setAssociatedObject(pointer, KEY, ref, 0)
  } else {
    debug('returning cached associated instance')
    rtn = p.getObject()
  }
  //assert.equal(rtn.pointer.address, pointer.address)
  return rtn
}

/*!
 * Internal function that essentially "creates" a new Function instance wrapping
 * the given pointer. The function's implementation is the "id()" function below,
 *
 * XXX: Maybe use `Function.create()` from my `create` module here (benchmark)???
 *
 * @api private
 */

function createFunctionWrapper (pointer) {
  debug('createFunctionWrapper(%d)', pointer.address)

  // This 'id' function is syntax sugar around the msgSend function attached to
  // it. 'msgSend' is expecting the selector first, an Array of args second, so
  // this function just massages it into place and returns the result.
  function id () {
    var args = []
      , argc = arguments.length
      , sel
    if (argc === 1) {
      var arg = arguments[0]
      if (arg.constructor.name == 'String') {
        // selector with no arguments
        sel = arg
      } else {
        // legacy API: an Object was passed in
        sel = []
        Object.keys(arg).forEach(function (s) {
          sel.push(s)
          args.push(arg[s])
        })
        sel.push('')
        sel = sel.join(':')
      }
    } else {
      // varargs API
      sel = []
      for (var i=0; i<argc; i+=2) {
        sel.push(arguments[i])
        args.push(arguments[i+1])
      }
      sel.push('')
      sel = sel.join(':')
    }
    return id.msgSend(sel, args)
  }

  // Set the "type" on the pointer. This is used by 'ref()' and 'unref()'.
  pointer._type = '@'
  // Save a reference to the pointer for use by the prototype functions
  id.pointer = pointer
  // Morph into a MUTANT FUNCTION FREAK!!1!
  id.__proto__ = proto
  return id
}

/**
 * A very important function that *does the message sending* between
 * Objective-C objects. When you do `array('addObject', anObject)`, this
 * `msgSend` function is the one that finally gets called to do the dirty work.
 *
 * This function accepts a String selector as the first argument, and an Array
 * of (wrapped) values that get passed to the the message. This function takes
 * care of unwrapping the passed in arguments and wrapping up the result value,
 * if necessary.
 */

proto.msgSend = function msgSend (sel, args) {
  debug('sending message:', sel, args)
  var types = this._getTypes(sel, args)
    , argTypes = types[1]
    , msgSendFunc = core.get_objc_msgSend(types)
    , unwrappedArgs = core.unwrapValues([this, sel].concat(args), argTypes)
    , rtn
  try {
    rtn = msgSendFunc.apply(null, unwrappedArgs)
  } catch (e) {
    throw exception.wrap(e)
  }
  // Process the return value into a wrapped value if needed
  return core.wrapValue(rtn, types[0])
}

/**
 * Accepts a SEL and queries the current object for the return type and
 * argument types for the given selector. If current object does not implment
 * that selector, then check the superclass, and repeat recursively until
 * a subclass that responds to the selector is found, or until the base class
 * is found.
 *
 * TODO: Just merge this logic with `msgSend()`? It's not used anywhere else
 *
 * @api private
 */

proto._getTypes = function getTypes (sel, args) {
  var c = this.getClass()
    , t = c._getTypesClass(sel, this.isClass)
  if (!t) {
    // Unknown selector being send to object. This *may* still be valid, we
    // assume all args are type 'id' and return is 'id'.
    debug('unknown selector being sent:', sel)
    t = [ '@', [ '@', ':', ].concat(args.map(function () { return '@' })) ]
  }
  return t
}

/**
 * Retrieves the wrapped Class instance for this object.
 */

proto.getClass = function getClass () {
  return Class.wrap(core.object_getClass(this.pointer))
}

/**
 * Calls 'object_getClassName()' on this object.
 */

proto.getClassName = function getClassName () {
  return core.object_getClassName(this.pointer)
}

/**
 * Dynamically changes the object's Class.
 */

proto.setClass = function setClass (newClass) {
  return Class.wrap(core.object_setClass(this.pointer, newClass.pointer))
}

/**
 * Walks up the inheritance chain and returns an Array of Strings of
 * superclasses.
 */

proto.ancestors = function ancestors () {
  var rtn = []
    , c = this.getClass()
  while (c) {
    rtn.push(c.getName())
    c = c.getSuperclass()
  }
  return rtn
}

/**
 * Getter/setter function for instance variables (ivars) of the object,
 * If just a name is passed in, then this function gets the ivar current value.
 * If a name and a new value are passed in, then this function sets the ivar.
 */

proto.ivar = function ivar (name, value) {
  // TODO: Add support for passing in a wrapped Ivar instance as the `name`
  if (arguments.length > 1) {
    // setter
    debug('setting ivar:', name, value)
    var ivar = this.isClass
             ? this.getClassVariable(name)
             : this.getClass().getInstanceVariable(name)
      , unwrapped = core.unwrapValue(value, ivar.getTypeEncoding())
    return core.object_setIvar(this.pointer, ivar.pointer, unwrapped)
  } else {
    // getter
    debug('getting ivar:', name)
    var ptr = new core.Pointer(core.TYPE_SIZE_MAP.pointer)
      , ivar = core.object_getInstanceVariable(this.pointer, name, ptr)
    return core.wrapValue(ptr.getPointer(), core.ivar_getTypeEncoding(ivar))
  }
}

/**
 * Returns an Array of Strings of the names of the ivars that the current object
 * contains. This function can iterate through the object's superclasses
 * recursively, if you specify a 'maxDepth' argument.
 */

proto.ivars = function ivars (maxDepth, sort) {
  var rtn = []
    , c = this.getClass()
    , md = maxDepth || 1
    , depth = 0
  while (c && depth++ < md) {
    var is = c.getInstanceVariables()
      , i = is.length
    while (i--) {
      if (!~rtn.indexOf(is[i])) rtn.push(is[i])
    }
    c = c.getSuperclass()
  }
  return sort === false ? rtn : rtn.sort()
}

/**
 * Returns an Array of Strings of the names of methods that the current object
 * will respond to. This function can iterate through the object's superclasses
 * recursively, if you specify a 'maxDepth' number argument.
 */

proto.methods = function methods (maxDepth, sort) {
  var rtn = []
    , c = this.getClass()
    , md = maxDepth || 1
    , depth = 0
  while (c && depth++ < md) {
    var ms = c.getInstanceMethods()
      , i = ms.length
    while (i--) {
      if (!~rtn.indexOf(ms[i])) rtn.push(ms[i])
    }
    c = c.getSuperclass()
  }
  return sort === false ? rtn : rtn.sort()
}

/**
 * Returns a **node-ffi** pointer pointing to this object. This is a convenience
 * function for methods that take pointers to objects (i.e. `NSError**`).
 *
 * @return {Pointer} A pointer to this object.
 */

proto.ref = function ref () {
  debug('id#ref()')
  var ptr = this.pointer.ref()
  return ptr
}

/**
 * The overidden `toString()` function proxies up to the real Objective-C object's
 * `description` method. In Objective-C, this is equivalent to:
 *
 *     [[id description] UTF8String]
 */

proto.toString = function toString () {
  return this('description')('UTF8String')
}

/*!
 * Custom inspect() function for `util.inspect()`.
 */

proto.inspect = function inspect () {
  return this.toString()
}
<|endoftext|>


<commit_before>/* Generic Node.js Database Library */

var is = module.exports = {};

/* Returns true if obj */
is.obj = function(obj) {
};

/* Returns true if `obj` instance of `what` */
is.objOf = function(obj, what) {
};

/* Returns true if array */
is.array = function(obj) {
};

/* Returns true if array */
is.string = function(obj) {
};

/* EOF */
<commit_msg>Updated<commit_after>/* Generic Node.js Database Library */

var is = module.exports = {};

is.true = function(x) {
	return x ? true : false;
}

/* Returns true if obj */
is.type = function(obj, type) {
	return is.true( typeof obj === type );
};

/* Returns true if obj */
is.obj = function(obj) {
	return is.true( (obj && is.type(obj, 'object')) );
};

/* Returns true if `obj` instance of `what` */
is.objOf = function(obj, what) {
	return is.true( is.obj(obj) && (obj instanceof what) );
};

/* Returns true if array */
is.array = function(obj) {
	return is.objOf(obj, Array);
};

/* Returns true if string */
is.string = function(obj) {
	return is.type(obj, 'string') || is.objOf(obj, String);
};

/* EOF */
<|endoftext|>


<commit_before>"use strict";
var comb = require("comb"),
    string = comb.string,
    multiply = string.multiply,
    hitch = comb.hitch,
    formatters = require("./formatters");

require("./extension");

var its = {};

var formatter = formatters.spec;
exports.__defineSetter__("reporter", function (f) {
    if (comb.isString(f)) {
        formatter = formatters[f];
    } else if (comb.isObject(f)
        && comb.isFunction(f.printTitle)
        && comb.isFunction(f.printSuccess)
        && comb.isFunction(f.printError)
        && comb.isFunction(f.printSummary)) {
        formatter = f;
    } else {
        throw new Error("Invalid formatter " + f);
    }
});

var setUpCb = function (cb) {
    return function () {
        var ret = new comb.Promise();
        var funcRet = new comb.Promise();
        var isCallback = false;
        var errorHandler = function (err) {
            !isCallback && ret.errback(err);
        };
        process.on("uncaughtException", errorHandler);
        try {
            var classicNext = function (err) {
                if (err) {
                    ret.errback(err);
                } else {
                    ret.callback();
                }
            };
            var l = cb.length;
            var response = cb.bind(funcRet)(classicNext, funcRet);
            if (comb.isPromiseLike(response)) {
                response.then(hitch(funcRet, "callback"), hitch(funcRet, "errback"));
            } else if (comb.isDefined(response) || l == 0) {
                !isCallback && ret.callback();
                isCallback = true;
            }
            funcRet.then(function () {
                !isCallback && ret.callback();
                isCallback = true;
            }, function (err) {
                !isCallback && ret.errback(err);
                isCallback = true;
            });
        } catch (err) {
            !isCallback && ret.errback(err);
            isCallback = true;
        }
        ret.both(function () {
            process.removeListener("uncaughtException", errorHandler);
        });
        return ret;
    }
};

var describeIt = function (description, sub, level) {
    var it = {

        __shoulds:[],

        __level:level || 0,

        __run:false,

        __ba:[],
        __be:[],
        __aa:[],
        __ae:[],
        __stopOnError:false,
        __summaries:{},
        __formatter:formatter,
        topic:description || "Test " + its.length + 1,

        beforeAll:function (cb) {
            this.__ba.push(setUpCb(cb));
            return it;
        },

        beforeEach:function (cb) {
            this.__be.push(setUpCb(cb));
            return it;
        },

        afterAll:function (cb) {
            this.__aa.push(setUpCb(cb));
            return it;
        },

        afterEach:function (cb) {
            this.__ae.push(setUpCb(cb));
            return it;
        },

        describe:function (description, cb) {
            var it = describeIt(description, true, this.__level + 1);
            it.__be = this.__be.slice();
            it.__ae = this.__ae.slice();
            it.__stopOnError = this.__stopOnError;
            it.__formatter = this.__formatter;
            var summaries = this.__summaries;
            "function" === typeof cb && cb(it);
            this.__shoulds.push(hitch(this, function () {
                var ret = new comb.Promise();
                it.run(this.__level + 1).both(function (summary) {
                    summaries[description] = summary;
                    ret.callback();
                });
                return ret;
            }));
            return it;
        },

        should:function (description, cb) {
            description = description || "Should " + this.__shoulds.length + 1;
            var summaries = this.__summaries, stopOnError = this.__stopOnError;
            var level = this.__level + 1;
            this.__shoulds.push(hitch(this, function () {
                var formatter = this.__formatter;
                var ret = new comb.Promise();
                var start = new Date();
                setUpCb(cb)().then(function () {
                    var end = new Date();
                    var sum = {description:description, start:start, end:end, duration:end - start, status:"passed", error:false};
                    summaries[description] = sum;
                    formatter.printSuccess(sum, level);
                    ret.callback();
                }, function (err) {
                    var end = new Date();

                    var sum = {
                        description:description,
                        start:start, end:end,
                        duration:end - start,
                        status:"failed",
                        error:err
                    };
                    var formatErr = formatter.printError(sum, level, err);
                    if (comb.isDefined(formatErr)) {
                        sum.status = formatErr === false ? "passed" : "failed";
                        sum.error = formatErr === false ? false : err;
                    }
                    summaries[description] = sum;

                    ret[stopOnError ? "errback" : "callback"](err);
                });
                return ret;
            }));
            return it;
        },

        run:function (level) {
            var ret = new comb.Promise();
            if (!this.__run) {
                this.__run = true;
                if (sub) {
                    level = level || 1;
                } else {
                    level = null;
                }
                var formatter = this.__formatter;
                formatter.printTitle(this.topic, level);
                var funcs = this.__ba.slice();
                this.__shoulds.forEach(function (batch) {
                    funcs = funcs.concat(this.__be);
                    funcs.push(batch);
                    funcs = funcs.concat(this.__ae);
                }, this);
                funcs = funcs.concat(this.__aa);
                var start = new Date();
                var summaries = this.__summaries;
                comb.serial(funcs).then(function () {
                    var duration = new Date() - start;
                    !sub && formatter.printSummary(summaries, duration);
                    this.__summaries = {summaries:summaries, duration:duration};
                    ret.callback(this.__summaries);
                }, function (err) {
                    this.__run = true;
                    var duration = new Date() - start;
                    formatter.printError("Top level error", 1, err);
                    !sub && formatter.printSummary(summaries, duration);
                    this.__summaries = {summaries:summaries, duration:duration};
                    ret.callback(this.__summaries);
                });
            } else {
                ret.callback(this.__summaries);
            }
            return ret;
        }
    };
    it.__defineSetter__("reporter", function (f) {
        if (comb.isString(f)) {
            this.__formatter = formatters[f];
        } else if (comb.isObject(f)
            && comb.isFunction(f.printTitle)
            && comb.isFunction(f.printSuccess)
            && comb.isFunction(f.printError)
            && comb.isFunction(f.printSummary)) {
            this.__formatter = f;
        } else {
            throw new Error("Invalid formatter " + f);
        }
    });

    it.__defineGetter__("reporter", function (f) {
        return this.__formatter;
    });
    sub !== true && (its[it.__topic] = it);
    return it;
};

exports.describe = function (description, cb) {
    var it = describeIt(description);
    "function" === typeof cb && cb(it);
    return it;
};

exports.run = function (opts) {
    return comb.serial(Object.keys(its).map(function (k) {
        return function () {
            return its[k].run();
        }
    }));
};

<commit_msg>fixed test lookup issue   * referenced wrong label<commit_after>"use strict";
var comb = require("comb"),
    string = comb.string,
    multiply = string.multiply,
    hitch = comb.hitch,
    formatters = require("./formatters");

require("./extension");

var its = {};

var formatter = formatters.spec;
exports.__defineSetter__("reporter", function (f) {
    if (comb.isString(f)) {
        formatter = formatters[f];
    } else if (comb.isObject(f)
        && comb.isFunction(f.printTitle)
        && comb.isFunction(f.printSuccess)
        && comb.isFunction(f.printError)
        && comb.isFunction(f.printSummary)) {
        formatter = f;
    } else {
        throw new Error("Invalid formatter " + f);
    }
});

var setUpCb = function (cb) {
    return function () {
        var ret = new comb.Promise();
        var funcRet = new comb.Promise();
        var isCallback = false;
        var errorHandler = function (err) {
            !isCallback && ret.errback(err);
        };
        process.on("uncaughtException", errorHandler);
        try {
            var classicNext = function (err) {
                if (err) {
                    ret.errback(err);
                } else {
                    ret.callback();
                }
            };
            var l = cb.length;
            var response = cb.bind(funcRet)(classicNext, funcRet);
            if (comb.isPromiseLike(response)) {
                response.then(hitch(funcRet, "callback"), hitch(funcRet, "errback"));
            } else if (comb.isDefined(response) || l == 0) {
                !isCallback && ret.callback();
                isCallback = true;
            }
            funcRet.then(function () {
                !isCallback && ret.callback();
                isCallback = true;
            }, function (err) {
                !isCallback && ret.errback(err);
                isCallback = true;
            });
        } catch (err) {
            !isCallback && ret.errback(err);
            isCallback = true;
        }
        ret.both(function () {
            process.removeListener("uncaughtException", errorHandler);
        });
        return ret;
    }
};

var describeIt = function (description, sub, level) {
    var it = {

        __shoulds:[],

        __level:level || 0,

        __run:false,

        __ba:[],
        __be:[],
        __aa:[],
        __ae:[],
        __stopOnError:false,
        __summaries:{},
        __formatter:formatter,
        topic:description || "Test " + its.length + 1,

        beforeAll:function (cb) {
            this.__ba.push(setUpCb(cb));
            return it;
        },

        beforeEach:function (cb) {
            this.__be.push(setUpCb(cb));
            return it;
        },

        afterAll:function (cb) {
            this.__aa.push(setUpCb(cb));
            return it;
        },

        afterEach:function (cb) {
            this.__ae.push(setUpCb(cb));
            return it;
        },

        describe:function (description, cb) {
            var it = describeIt(description, true, this.__level + 1);
            it.__be = this.__be.slice();
            it.__ae = this.__ae.slice();
            it.__stopOnError = this.__stopOnError;
            it.__formatter = this.__formatter;
            var summaries = this.__summaries;
            "function" === typeof cb && cb(it);
            this.__shoulds.push(hitch(this, function () {
                var ret = new comb.Promise();
                it.run(this.__level + 1).both(function (summary) {
                    summaries[description] = summary;
                    ret.callback();
                });
                return ret;
            }));
            return it;
        },

        should:function (description, cb) {
            description = description || "Should " + this.__shoulds.length + 1;
            var summaries = this.__summaries, stopOnError = this.__stopOnError;
            var level = this.__level + 1;
            this.__shoulds.push(hitch(this, function () {
                var formatter = this.__formatter;
                var ret = new comb.Promise();
                var start = new Date();
                setUpCb(cb)().then(function () {
                    var end = new Date();
                    var sum = {description:description, start:start, end:end, duration:end - start, status:"passed", error:false};
                    summaries[description] = sum;
                    formatter.printSuccess(sum, level);
                    ret.callback();
                }, function (err) {
                    var end = new Date();

                    var sum = {
                        description:description,
                        start:start, end:end,
                        duration:end - start,
                        status:"failed",
                        error:err
                    };
                    var formatErr = formatter.printError(sum, level, err);
                    if (comb.isDefined(formatErr)) {
                        sum.status = formatErr === false ? "passed" : "failed";
                        sum.error = formatErr === false ? false : err;
                    }
                    summaries[description] = sum;

                    ret[stopOnError ? "errback" : "callback"](err);
                });
                return ret;
            }));
            return it;
        },

        run:function (level) {
            var ret = new comb.Promise();
            if (!this.__run) {
                this.__run = true;
                if (sub) {
                    level = level || 1;
                } else {
                    level = null;
                }
                var formatter = this.__formatter;
                formatter.printTitle(this.topic, level);
                var funcs = this.__ba.slice();
                this.__shoulds.forEach(function (batch) {
                    funcs = funcs.concat(this.__be);
                    funcs.push(batch);
                    funcs = funcs.concat(this.__ae);
                }, this);
                funcs = funcs.concat(this.__aa);
                var start = new Date();
                var summaries = this.__summaries;
                comb.serial(funcs).then(function () {
                    var duration = new Date() - start;
                    !sub && formatter.printSummary(summaries, duration);
                    this.__summaries = {summaries:summaries, duration:duration};
                    ret.callback(this.__summaries);
                }, function (err) {
                    this.__run = true;
                    var duration = new Date() - start;
                    formatter.printError("Top level error", 1, err);
                    !sub && formatter.printSummary(summaries, duration);
                    this.__summaries = {summaries:summaries, duration:duration};
                    ret.callback(this.__summaries);
                });
            } else {
                ret.callback(this.__summaries);
            }
            return ret;
        }
    };
    it.__defineSetter__("reporter", function (f) {
        if (comb.isString(f)) {
            this.__formatter = formatters[f];
        } else if (comb.isObject(f)
            && comb.isFunction(f.printTitle)
            && comb.isFunction(f.printSuccess)
            && comb.isFunction(f.printError)
            && comb.isFunction(f.printSummary)) {
            this.__formatter = f;
        } else {
            throw new Error("Invalid formatter " + f);
        }
    });

    it.__defineGetter__("reporter", function (f) {
        return this.__formatter;
    });
    sub !== true && (its[it.topic] = it);
    return it;
};

exports.describe = function (description, cb) {
    var it = describeIt(description);
    "function" === typeof cb && cb(it);
    return it;
};

exports.run = function (opts) {
    return comb.serial(Object.keys(its).map(function (k) {
        return function () {
            return its[k].run();
        }
    }));
};

<|endoftext|>


<commit_before>// Dependencies
var TxnSender = require('./txn_sender.js')
var Call      = require('./call.js')
var proto     = require('./proto.js')
var retry     = require('./retry.js')

// TxnRetryOptions sets the retry options for handling write conflicts.
var TxnRetryOptions = new retry.Options({
    backoff:     50, // ms
    maxBackoff:  5000,
    constant:    2,
    maxAttempts: 0, // retry indefinitely
})

// Returns you a KV rpc client
// @constructor
// @param {sender} - http request client
// @param {clock}  - clock module
function KV(sender, clock) {
    // User is the default user to set on API calls. If User is set to
    // non-empty in call arguments, this value is ignored.
    this.user = 'root'
    // UserPriority is the default user priority to set on API calls. If
    // UserPriority is set non-zero in call arguments, this value is
    // ignored.
    this.userPriority = 0 // int32

    this.sender = sender
    this.clock  = clock

    this.prepareBuffer = []

    return this
}

// Sender returns the sender supplied to NewKV, unless wrapped by a
// transactional sender, in which case returns the unwrapped sender.
KV.prototype.Sender = function Sender() {
    if(this.sender instanceof TxnSender) {
        return this.wrapped
    }
    else {
        return this.sender
    }
}

// Call invokes the KV command synchronously and returns the response
// and error, if applicable. If preceeding calls have been made to
// Prepare() without a call to Flush(), this call is prepared and
// then all prepared calls are flushed.
KV.prototype.Call = function KVCall(method, request, callback) {
    var header = request.getHeader()

    if(! header.get('user')) {
        header.set('user', this.user)
    }
    if(this.userPriority > 0 && ! header.get('user_priority')) {
        header.set('user_priority', this.userPriority)
    }

    var call = new Call({
        Method: method,
        Request: request,
    })
    call.resetClientCmdID(this.clock)

    this.sender.Send(call, function(err, res) {
        if(err) {
            callback(err, null)
            return
        }

        if(!res) {
            callback(new Error('expected an res object, received nothing'), null)
            return
        }

        var header = res.header

        if(!header) {
            var error = new Error('res object had no header, expected an header')
        }
        else {
            var error = header.error
        }

        if(error) {
            callback(error, res)
            return
        }

        callback(null, res)
    })
}

// Prepare accepts a KV API call, specified by method name, arguments
// and a reply struct. The call will be buffered locally until the
// first call to Flush(), at which time it will be sent for execution
// as part of a batch call. Using Prepare/Flush parallelizes queries
// and updates and should be used where possible for efficiency.
//
// For clients using an HTTP sender, Prepare/Flush allows multiple
// commands to be sent over the same connection. For transactional
// clients, Prepare/Flush can dramatically improve efficiency by
// compressing multiple writes into a single atomic update in the
// event that the writes are to keys within a single range. However,
// using Prepare/Flush alone will not guarantee atomicity. Clients
// must use a transaction for that purpose.
//
// The supplied reply struct will not be valid until after a call
// to Flush().
KV.prototype.Prepare = function Prepare(method, request, callback) {
    // Make callback optional
    if(typeof callback == 'undefined') {
        callback = empty_callback
    }

    // Validate callback
    if(callback instanceof Function) {
        this.prepareBuffer.push(new Call({
            Method: method,
            Request: request,
            Callback: callback,
        }))
    }
    // Not a valid callback
    else {
        throw new Error('invalid callback, check your code! received a: ' + typeof callback)
    }
}

// Flush sends all previously prepared calls, buffered by invocations
// of Prepare(). The calls are organized into a single batch command
// and sent together. Flush returns nil if all prepared calls are
// executed successfully. Otherwise, Flush returns the first error,
// where calls are executed in the order in which they were prepared.
// After Flush returns, all prepared reply structs will be valid.
KV.prototype.Flush = function Flush(callback) {
    if(! callback) {
        callback = empty_callback
    }

    if(this.prepareBuffer.length === 0) {
        callback(null)
        return
    }
    else if (this.prepareBuffer.length === 1) {
        var call = this.prepareBuffer[0]
        this.Call(call.Method, call.Request, call.Callback)
        this.prepareBuffer = []
        callback(null)
        return
    }

    // Copy flush buffer somewhere else
    // (or at least keep a reference so it won't be GC)
    var batch = []
    for(var i in this.prepareBuffer) {
        batch[i] = this.prepareBuffer[i]
    }

    // Clear buffer
    this.prepareBuffer = []

    // Make call
    this.Call(proto.Batch, new proto.BatchArgs(batch), function FlushCallback(err, res) {
        if(err) {
            callback(err)
            return
        }

        for(var i in res.responses) {
            var call = batch[i]
            var method = call.Method
            var resp = res.responses[i][proto.BatchFindWrapperProp(method)]
            var header = resp.header

            batch[i].Callback(header.error, resp)
        }

        callback(null)
    })
}

// RunTransaction executes retryable in the context of a distributed
// transaction. The transaction is automatically aborted if retryable
// returns any error aside from recoverable internal errors, and is
// automatically committed otherwise. retryable should have no side
// effects which could cause problems in the event it must be run more
// than once. The opts struct contains transaction settings.
//
// Calling RunTransaction on the transactional KV client which is
// supplied to the retryable function is an error.
KV.prototype.RunTransaction = function RunTransaction(opts, retryable, callback) {
    if(this.sender instanceof TxnSender) {
        throw new Error('cannot invoke RunTransaction on an already-transactional client')
    }

    // Create a new KV for the transaction using a transactional KV sender.
    var txnSender = new TxnSender(this.sender, {
        name: opts.name || '',
        isolation: opts.isolation || proto.SERIALIZABLE,
    })

    var txnKV = new KV(txnSender, this.clock)
    txnKV.user = this.user
    txnKV.userPriority = this.userPriority

    // Run retryable in a retry loop until we encounter a success or
    // error condition this loop isn't capable of handling.
    var retryOpts = TxnRetryOptions
    retryOpts.tag = opts.name
    retry.WithBackoff(retryOpts, function Retry(retryCallback) {
        txnSender.txnEnd = false // always reset before [re]starting txn

        // User is trying to commit the trasaction
        var commit = function Commit() {
            if(txnSender.txnEnd === false) {
                var request = new proto.EndTransactionRequest({
                    header: new proto.RequestHeader(), // avoid using null headers
                    commit: true,
                })

                txnKV.Prepare(proto.EndTransaction, request, function txnResponseCallback(err, res) {
                    // TODO: i'm not sure what this err is
                    // maybe this err is only for http connections errors
                    // I'm not sure flush returns real db errors
                    if(err instanceof proto.ReadWithinUncertaintyIntervalError) {
                        // Retry immediately on read within uncertainty interval.
                        retryCallback(null, retry.Reset)
                    }
                    else if(err instanceof proto.TransactionAbortedError) {
                        // If the transaction was aborted, the txnSender will have created
                        // a new txn. We allow backoff/retry in this case.
                        retryCallback(null, retry.Continue)
                    }
                    else if(err instanceof proto.TransactionPushError) {
                        // Backoff and retry on failure to push a conflicting transaction.
                        retryCallback(null, retry.Continue)
                    }
                    else if(err instanceof proto.TransactionRetryError) {
                        // Return RetryReset for an immediate retry (as in the case of
                        // an SSI txn whose timestamp was pushed).
                        retryCallback(null, retry.Reset)
                    }
                    else {
                        // For all other cases, finish retry loop, returning possible error.
                        retryCallback(err, retry.Break)
                    }
                })

                txnKV.Flush(function(err) {
                    if(err) {
                        // Failed to run transaction batch
                        // Abort everything...
                        retryCallback(err, retry.Break)
                    }
                })
            }
            else {
                // Break retry loop
                // Someone else has ended the transaction
                retryCallback(null, retry.Break)
            }
        }
        // Something when wrong, user is aborting
        var abort = function Abort(err) {
            err = err || new Error('transacation aborted by user, no error was specified.')

            retryCallback(err, retry.Break)
        }

        // Run retryable transaction fnc
        try {
            retryable(txnKV, commit, abort)
        } catch(e) {
            // Abort the transaction
            abort(e)
        }
    }, function Finish(err) {
        if(err && txnSender.txnEnd === false) {
            var request = new proto.EndTransactionRequest({
                header: new proto.RequestHeader(),
                commit: false,
            })
            var txnErr = err

            txnKV.Call(proto.EndTransaction, request, function(err, res) {
                if(err) {
                    console.log('failure aborting transaction: %s; abort caused by: %s', txnSender.txn.name, txnErr)
                }

                txnKV.Close()
                txnSender.txnEnd = true

                callback(err, res)
            })
            return
        }

        txnKV.Close()

        callback(err)
    })
}

// Close sender
KV.prototype.Close = function Close() {
    this.sender.Close()
}

// Helper fnc, reference to an empty callback
function empty_callback() {}

// Exports
module.exports = KV
<commit_msg>Don't swallow transaction exceptions.<commit_after>// Dependencies
var TxnSender = require('./txn_sender.js')
var Call      = require('./call.js')
var proto     = require('./proto.js')
var retry     = require('./retry.js')

// TxnRetryOptions sets the retry options for handling write conflicts.
var TxnRetryOptions = new retry.Options({
    backoff:     50, // ms
    maxBackoff:  5000,
    constant:    2,
    maxAttempts: 0, // retry indefinitely
})

// Returns you a KV rpc client
// @constructor
// @param {sender} - http request client
// @param {clock}  - clock module
function KV(sender, clock) {
    // User is the default user to set on API calls. If User is set to
    // non-empty in call arguments, this value is ignored.
    this.user = 'root'
    // UserPriority is the default user priority to set on API calls. If
    // UserPriority is set non-zero in call arguments, this value is
    // ignored.
    this.userPriority = 0 // int32

    this.sender = sender
    this.clock  = clock

    this.prepareBuffer = []

    return this
}

// Sender returns the sender supplied to NewKV, unless wrapped by a
// transactional sender, in which case returns the unwrapped sender.
KV.prototype.Sender = function Sender() {
    if(this.sender instanceof TxnSender) {
        return this.wrapped
    }
    else {
        return this.sender
    }
}

// Call invokes the KV command synchronously and returns the response
// and error, if applicable. If preceeding calls have been made to
// Prepare() without a call to Flush(), this call is prepared and
// then all prepared calls are flushed.
KV.prototype.Call = function KVCall(method, request, callback) {
    var header = request.getHeader()

    if(! header.get('user')) {
        header.set('user', this.user)
    }
    if(this.userPriority > 0 && ! header.get('user_priority')) {
        header.set('user_priority', this.userPriority)
    }

    var call = new Call({
        Method: method,
        Request: request,
    })
    call.resetClientCmdID(this.clock)

    this.sender.Send(call, function(err, res) {
        if(err) {
            callback(err, null)
            return
        }

        if(!res) {
            callback(new Error('expected an res object, received nothing'), null)
            return
        }

        var header = res.header

        if(!header) {
            var error = new Error('res object had no header, expected an header')
        }
        else {
            var error = header.error
        }

        if(error) {
            callback(error, res)
            return
        }

        callback(null, res)
    })
}

// Prepare accepts a KV API call, specified by method name, arguments
// and a reply struct. The call will be buffered locally until the
// first call to Flush(), at which time it will be sent for execution
// as part of a batch call. Using Prepare/Flush parallelizes queries
// and updates and should be used where possible for efficiency.
//
// For clients using an HTTP sender, Prepare/Flush allows multiple
// commands to be sent over the same connection. For transactional
// clients, Prepare/Flush can dramatically improve efficiency by
// compressing multiple writes into a single atomic update in the
// event that the writes are to keys within a single range. However,
// using Prepare/Flush alone will not guarantee atomicity. Clients
// must use a transaction for that purpose.
//
// The supplied reply struct will not be valid until after a call
// to Flush().
KV.prototype.Prepare = function Prepare(method, request, callback) {
    // Make callback optional
    if(typeof callback == 'undefined') {
        callback = empty_callback
    }

    // Validate callback
    if(callback instanceof Function) {
        this.prepareBuffer.push(new Call({
            Method: method,
            Request: request,
            Callback: callback,
        }))
    }
    // Not a valid callback
    else {
        throw new Error('invalid callback, check your code! received a: ' + typeof callback)
    }
}

// Flush sends all previously prepared calls, buffered by invocations
// of Prepare(). The calls are organized into a single batch command
// and sent together. Flush returns nil if all prepared calls are
// executed successfully. Otherwise, Flush returns the first error,
// where calls are executed in the order in which they were prepared.
// After Flush returns, all prepared reply structs will be valid.
KV.prototype.Flush = function Flush(callback) {
    if(! callback) {
        callback = empty_callback
    }

    if(this.prepareBuffer.length === 0) {
        callback(null)
        return
    }
    else if (this.prepareBuffer.length === 1) {
        var call = this.prepareBuffer[0]
        this.Call(call.Method, call.Request, call.Callback)
        this.prepareBuffer = []
        callback(null)
        return
    }

    // Copy flush buffer somewhere else
    // (or at least keep a reference so it won't be GC)
    var batch = []
    for(var i in this.prepareBuffer) {
        batch[i] = this.prepareBuffer[i]
    }

    // Clear buffer
    this.prepareBuffer = []

    // Make call
    this.Call(proto.Batch, new proto.BatchArgs(batch), function FlushCallback(err, res) {
        if(err) {
            callback(err)
            return
        }

        for(var i in res.responses) {
            var call = batch[i]
            var method = call.Method
            var resp = res.responses[i][proto.BatchFindWrapperProp(method)]
            var header = resp.header

            batch[i].Callback(header.error, resp)
        }

        callback(null)
    })
}

// RunTransaction executes retryable in the context of a distributed
// transaction. The transaction is automatically aborted if retryable
// returns any error aside from recoverable internal errors, and is
// automatically committed otherwise. retryable should have no side
// effects which could cause problems in the event it must be run more
// than once. The opts struct contains transaction settings.
//
// Calling RunTransaction on the transactional KV client which is
// supplied to the retryable function is an error.
KV.prototype.RunTransaction = function RunTransaction(opts, retryable, callback) {
    if(this.sender instanceof TxnSender) {
        throw new Error('cannot invoke RunTransaction on an already-transactional client')
    }

    // Create a new KV for the transaction using a transactional KV sender.
    var txnSender = new TxnSender(this.sender, {
        name: opts.name || '',
        isolation: opts.isolation || proto.SERIALIZABLE,
    })

    var txnKV = new KV(txnSender, this.clock)
    txnKV.user = this.user
    txnKV.userPriority = this.userPriority

    // Run retryable in a retry loop until we encounter a success or
    // error condition this loop isn't capable of handling.
    var retryOpts = TxnRetryOptions
    retryOpts.tag = opts.name
    retry.WithBackoff(retryOpts, function Retry(retryCallback) {
        txnSender.txnEnd = false // always reset before [re]starting txn

        // User is trying to commit the trasaction
        var commit = function Commit() {
            if(txnSender.txnEnd === false) {
                var request = new proto.EndTransactionRequest({
                    header: new proto.RequestHeader(), // avoid using null headers
                    commit: true,
                })

                txnKV.Prepare(proto.EndTransaction, request, function txnResponseCallback(err, res) {
                    // TODO: i'm not sure what this err is
                    // maybe this err is only for http connections errors
                    // I'm not sure flush returns real db errors
                    if(err instanceof proto.ReadWithinUncertaintyIntervalError) {
                        // Retry immediately on read within uncertainty interval.
                        retryCallback(null, retry.Reset)
                    }
                    else if(err instanceof proto.TransactionAbortedError) {
                        // If the transaction was aborted, the txnSender will have created
                        // a new txn. We allow backoff/retry in this case.
                        retryCallback(null, retry.Continue)
                    }
                    else if(err instanceof proto.TransactionPushError) {
                        // Backoff and retry on failure to push a conflicting transaction.
                        retryCallback(null, retry.Continue)
                    }
                    else if(err instanceof proto.TransactionRetryError) {
                        // Return RetryReset for an immediate retry (as in the case of
                        // an SSI txn whose timestamp was pushed).
                        retryCallback(null, retry.Reset)
                    }
                    else {
                        // For all other cases, finish retry loop, returning possible error.
                        retryCallback(err, retry.Break)
                    }
                })

                txnKV.Flush(function(err) {
                    if(err) {
                        // Failed to run transaction batch
                        // Abort everything...
                        retryCallback(err, retry.Break)
                    }
                })
            }
            else {
                // Break retry loop
                // Someone else has ended the transaction
                retryCallback(null, retry.Break)
            }
        }
        // Something when wrong, user is aborting
        var abort = function Abort(err) {
            err = err || new Error('transacation aborted by user, no error was specified.')

            retryCallback(err, retry.Break)
        }

        // Run retryable transaction fnc
        try {
            retryable(txnKV, commit, abort)
        } catch(e) {
            // Abort the transaction
            abort(e)
        }
    }, function Finish(err) {
        if(err && txnSender.txnEnd === false) {
            var request = new proto.EndTransactionRequest({
                header: new proto.RequestHeader(),
                commit: false,
            })
            var txnErr = err

            txnKV.Call(proto.EndTransaction, request, function(err, res) {
                if(err) {
                    console.log('failure aborting transaction: %s; abort caused by: %s', txnSender.txn.name, txnErr)
                }

                txnKV.Close()
                txnSender.txnEnd = true

                callback(err || txnErr, res)
            })
            return
        }

        txnKV.Close()

        callback(err)
    })
}

// Close sender
KV.prototype.Close = function Close() {
    this.sender.Close()
}

// Helper fnc, reference to an empty callback
function empty_callback() {}

// Exports
module.exports = KV
<|endoftext|>