Socket
Socket
Sign inDemoInstall

qbus

Package Overview
Dependencies
0
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.9.6 to 0.9.7

58

benchmark/index.js

@@ -6,38 +6,44 @@ var Benchmark = require('benchmark'),

Qbus1 = require('../index'),
Qbus2 = require('../index'),
Qbus2 = require('./qbus2.js'),
qbus1 = new Qbus1(),
qbus2 = new Qbus2(),
qbus2 = new Qbus2();
EventEmitter = require('events');
nodeEmitter = new EventEmitter();
nodeEmitter.setMaxListeners(Infinity);
function noop () {}
function woop () { console.log('woop'); }
function woop () { console.log('woop', arguments); }
var e = ['b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q'],
y;
// Listen on all string queries
queries.simple.forEach(function (query) {
qbus1.on(query, noop);
qbus2.on(query, noop);
});
var to = 1000000;
while (to--)
qbus1.on('a', noop);
// Listen on all regexp queries
queries.complex.forEach(function (query) {
qbus1.on(query, noop);
qbus2.on(query, noop);
});
qbus1.on('b', noop);
console.log('Each run consists of ' + queries.simple.length + ' emits against ' + (queries.simple.length + queries.complex.length) + ' listeners.');
suite
.add('qbus 1', function () {
queries.simple.forEach(function (query) {
qbus1.emit(query, 'a', 'b', 'c');
});
})
.add('qbus 2', function() {
queries.simple.forEach(function (query) {
qbus2.emit(query, 'a', 'b', 'c');
});
})
.on('cycle', function(event) {
console.log(String(event.target));
})
.on('complete', function() {
console.log('Fastest is ' + this.filter('fastest').pluck('name'));
})
.run();
(new Benchmark.Suite)
.add('qbus', function () {
qbus1.emit('b');
})
.on('cycle', function(event) {
console.log(String(event.target));
})
.run();
// require('fs').writeFile(__dirname + '/qbus1.out.txt', JSON.stringify(qbus1.qbus, function (key, val) { return (val instanceof RegExp ? val.source : val); }, '\t'));
// require('fs').writeFile(__dirname + '/qbus2.out.txt', JSON.stringify(qbus2.qbus, function (key, val) { return (val instanceof RegExp ? val.source : val); }, '\t'));
{
"name": "qbus",
"description": "Minimalistic and fast isomorphic mediator with dynamic queries",
"version": "v0.9.6",
"version": "v0.9.7",
"author": "Pehr Boman <unkelpehr@gmail.com>",

@@ -6,0 +6,0 @@ "homepage": "https://github.com/unkelpehr/qbus",

@@ -5,3 +5,3 @@ # qbus

- AMD & CommonJS compatible
- Works in browser and Node completely without hacks, polyfills etc
- Works in browser and node completely without hacks, polyfills etc
- Extensive test suite with 1.500+ tests

@@ -11,4 +11,10 @@ - No dependencies - one file

- Small (~1.5KB gzipped)
- V8 optimized
**Node**
[TOC]
<a href="http://unkelpehr.github.io/qbus" target="_blank">Try a live demo of qbus query parser here</a>
##Installation
###Node
```sh

@@ -18,14 +24,21 @@ $ npm install qbus

<a target="_blank" href="https://marijnhaverbeke.nl/uglifyjs?utf8=on&code_url=https://raw.githubusercontent.com/unkelpehr/qbus/master/lib/index.js&header=/**%20@license%20Licenced%20under%20MIT%20-%20qbus%20-%20%C2%A92015%20Pehr%20Boman%20%3Cgithub.com/unkelpehr%3E%20*/">Browser? Get the latest version of qbus minified and ready to-go here</a>
###Browser
Download the latest uncompressed version in _<a href="https://github.com/unkelpehr/qbus/blob/master/lib/index.js">lib/index.js</a>_.
##### <a href="http://unkelpehr.github.io/qbus" target="_blank">You can also try a live demo of qbus' query parser here</a>
<a target="_blank" href="https://marijnhaverbeke.nl/uglifyjs?utf8=on&code_url=https://raw.githubusercontent.com/unkelpehr/qbus/master/lib/index.js&header=/**%20@license%20Licenced%20under%20MIT%20-%20qbus%20-%20%C2%A92015%20Pehr%20Boman%20%3Cgithub.com/unkelpehr%3E%20*/">Or get it compressed using UglifyJS here</a> _via <a href="https://marijnhaverbeke.nl/" target="_blank">https://marijnhaverbeke.nl/</a>_
### Behaviour
Queries can be anything but Qbus is built with a "path-based" approach for routing e.g. URLs.
Frontslashes at the beginning and end of the query is ignored. I.e. these queries are identical: `/get/stuff/`, `get/stuff/`, `/get/stuff`, `get/stuff`. The only exception to this rule is when working with wildcards - more on that below.
## General
###Terminology
_Expressions_ are given to the _.on_ and _.once_ methods for matching incoming _queries_.
_Queries_ are given to the _.emit_ function to be ran against the stored _expressions_ looking for a match.
###Introduction
Any string can be an expression but qbus is built with a "path-based" approach _(foo/bar/baz)_ for routing e.g. URLs.
Frontslashes at the beginning and end of the query are ignored. I.e. these queries are identical: `/get/stuff/`, `get/stuff/`, `/get/stuff`, `get/stuff`. The only exception to this rule is when working with wildcards - more on that below.
All queries are as of now case insensitive, an option to control that will probably come in the future.
The context for all `handler` functions is the Qbus object or the `parent` object that was passed to the Qbus constructor (more on that under _Parasitic inheritence_).
The context for all `handler` functions is the qbus object or the `parent` object that was passed to the qbus constructor (more on that under _Parasitic inheritence_).

@@ -35,5 +48,5 @@ Params extracted from the query will be the first arguments to the `handler` function. Subsequent arguments comes from the emitter:

```js
bus.on('/:a/:b/:c', function () {
console.log(arguments); // [a, b, c, d, e, f]
}).emit('/a/b/c/', 'd', 'e', 'f');
bus.on('/:one/:two?/:three', function () {
console.log(arguments); // ['one', 'two', 'three', 'four', 'five', 'six']
}).emit('/one/two/three/', 'four', 'five', 'six');
```

@@ -43,37 +56,14 @@

```js
bus.on('/:a/:b?/:c?', function () {
console.log(arguments); // [a, undefined, undefined, d, e, f]
}).emit('/a/', 'd', 'e', 'f');
bus.on('/:one/:two?/:three', function () {
console.log(arguments); // ['one', undefined, 'three', 'four', 'five', 'six']
}).emit('/one/three/', 'four', 'five', 'six');
```
## Disadvantages from using a regular eventemitter
The advantages of dynamic queries ("_events"_) are many. But what are the disadvantages?
##### Wondering how Qbus interprets your queries?
The `parse` function is exposed in the non-enumerable property "qbus":
Event emitters with static _event names_ are really simple and have unprecedented speed. This is because they can store the event handlers in a `key: [_handlers_]`-fashion:
```js
var regexp = bus.qbus.parse('/users/:userId/');
console.log(regexp instanceof RegExp, regexp);
// true { /^/?users/([^/]+?)/?$/i query: [Function: execQuery] }
```
##### What's `query` doing there?
When matching regex the first match is always the whole string. I.e.
```js
/^/?users/([^/]+?)/?$/i.exec('/users/13'); => ['/users/13', '13']
```
All that 'query' does is shifting the first (whole) match to give the listener exactly what it wants:
```js
var params = /^/?users/([^/]+?)/?$/i.query('/users/13'); => ['13']
handler.apply(this, params.concat(emitArgs));
```
### Disadvantages from using static queries
"Ordinary" event emitters with static queries are really simple and have unprecedented speed. This is because they can store the events in a lookup-object:
```js
var subscriptions = {
var listeners = {
hover: [Function, Function, Function],

@@ -90,3 +80,3 @@ click: [Function]

if (!(handlers = subscriptions[event])) {
if (!(handlers = listeners[event])) {
return; // No listeners for this event

@@ -102,3 +92,3 @@ }

We can't do this. Because our listeners are listening to e.g. `/admin/users/:userId?/:action`. What usually happens behind the scenes is that the subscriptions is added to an array which is then looped on each `emit` to match all stored subscriptions against the emitted query:
We can't do this because our listeners are listening to e.g. `/admin/users/:userId?/:action`. What usually happens behind the scenes is that the listeners are pushed to an array which is then looped on each `emit` to match all stored expressions against the emitted query:

@@ -121,3 +111,3 @@ ```js

i = 0;
while ((sub = subscriptions[i++])) {
while ((sub = listeners[i++])) {
if ((match = sub.re.exec(query))) {

@@ -130,8 +120,8 @@ sub.handler.apply(this, match.concat(args));

So if we have ten listeners each on `/admin/users`, `/admin/groups`, `/admin/articles` every emit against `/admin/users` will have to be matched against 30 RegExp objects before running out.
So if we have ten listeners each on `/admin/users`, `/admin/groups` and `/admin/articles` every emit against `/admin/users` will have to be matched against 30 RegExp objects before running out.
##### What we can do about it
The first thing Qbus does to crank up the ops/s is to use string comparison instead of RegExp.exec if the listener-query doesn't use any modifiers.
#### What we can do about it
The first thing qbus does to crank up the emitrate is to use string comparison instead of RegExp.exec if the listener-query doesn't use any modifiers.
The second thing we do is to break out the fixed part of the query and store them individually. So instead of the mega-array mentioned above we can pinpoint and check multiple, smaller arrays for more exact matching.
The second thing we do is to extract the fixed part of the expression and use it for namespacing. So instead of the mega-array mentioned above we can pinpoint and check multiple, smaller arrays for more exact matching.

@@ -184,5 +174,5 @@ ```js

needle = popNeedle(needle); // "admin/users/joe/"
// "admin/users/"
// "admin/"
needle = popNeedle(needle); // "/admin/users/joe"
// "/admin/users"
// "/admin"
// "/"

@@ -193,23 +183,29 @@ }

So now, instead of looping through `/admin/groups` and `/admin/articles` looking for subscriptions, we pinpointed potential subscribers and went from 30 iterations to 10 + 4. This is a huge speed improvement when the listener count is growing.
So instead of looping through `/admin/groups` and `/admin/articles` looking for subscriptions we now pinpointed potential listeners and went from 30 iterations to 10 + 4. This is a huge speed improvement when the listener count is growing.
The last functionality we can use to speed up routing is to break the loop from within a handler; the equivalent of using `e.preventDefault(); e.stopPropagation()` for DOM events. The natural drawback is that if a handler breaks at `/admin/users/joe/` no handlers at `admin/users/`, `admin/` and `/` will have a chance to execute.
The last functionality we can use to speed up routing is to break the loop from within a handler; the equivalent of using `e.stopPropagation()` for DOM events. The natural drawback is that if a handler breaks at `/admin/users/joe/` no handlers at `admin/users/`, `admin/` and `/` will have a chance to execute.
```js
bus.on('/admin/users/joe', function () {
return false; // Tell Qbus to stop the emit loop
return false; // Tell qbus to stop the emit loop
});
```
##### What get stored at "/"?
Everything that doesn't begin with a predeterminable portion. I.e. `on('/*')`, `on('/*stuff/')`, `on('/:page')` but also all pure RegExp subscriptions. You'd want to keep this collection small as they will always have to be matched against.
#### What get stored at "/"?
Everything that doesn't begin with a predeterminable portion. I.e. `on('/*')`, `on('/*stuff/')`, `on('/:page')` but also all pure RegExp subscriptions. You'd want to keep this collection small as the expressions stored here will always be matched against.
You can check how your queries are being stored by logging `bus.qbus.paths`.
##### Sooo... how fast is it?
That depends on the complexity and quantity of the listener base that emits has to be matched against. But as a rule of thumb - avoid queries that can't be predetermined (like the ones listed above) and use namespacing to allow Qbus to break up the listeners.
#### Sooo... how fast is it?
That depends a lot on the complexity and quantity of the listener base. But as a rule of thumb if speed is an issue: avoid `/`, avoid deep paths (every level needs to be checked) and keep all dynamic portions of the expressions far to the right. These for example will all be stored at the same level: `/users`, `/users/:userId?`, `/users/:userId?/:action`, `/user/*` and all has to be matched on any query with `/users` as basename.
My 4 year old, then upper medium-end laptop can emit('/users/4') against ('/users/:id?', noop) 500.000 times per second.
The amount of listeners __not__ tuning in on the same static path does not affect eachother. 1 or 1m listeners on `a` does not affect to rate of emitting against `b`.
### Usage
On this current machine, single core i5 2.90GHz processor running with W7, using a possible old qbus version when you read this:
4,891,286 emits/sec with 1 listener and a static query<br>
4,749,031 emits/sec with 1 listener and a static query _and 1m listeners in another basedir_<br>
2,914,642 emits/sec with 1 listener and a dynamic query<br>
## Usage
```js

@@ -220,3 +216,3 @@ var Qbus = require('Qbus'),

##### Parasitic inheritance
### Parasitic inheritance
Qbus will latch on to any object ("_parent_") passed to it's constructor. This is a simple way of extending your own modules with Qbus' functionality. Four functions will be added to `parent`'s properties: `on`, `once`, `off`, `emit` along with a non-enumerable object called `qbus`; where all the subscriptions will be stored.

@@ -248,3 +244,3 @@ ```js

##### .on(<`query`= String|RegExp>, <`handler` = Function>)
### .on(<`query`= String|RegExp>, <`handler` = Function>)
Let `handler` execute on given `query`.

@@ -258,3 +254,3 @@ ```js

##### .emit(<`query`= String>[, <`arg1` = *>, <`arg2` = *>, ...])
### .emit(<`query`= String>[, <`arg1` = *>, <`arg2` = *>, ...])
Execute all handlers that matches `query`. Arguments after the mandatory first `query` will be passed to each handler.

@@ -270,3 +266,3 @@ ```js

```
##### .off(<`query`= String|RegExp>[, <`handler` = Function>])
### .off(<`query`= String|RegExp>[, <`handler` = Function>])
Remove all listeners with a query that matches `query` and a handler that matches `handler`. If `handler` is undefined all subscriptions for `query` will be removed.

@@ -281,3 +277,3 @@ ```js

##### .once(<`query`= String|RegExp>, <`handler` = Function>)
### .once(<`query`= String|RegExp>, <`handler` = Function>)
Identical to `on` but the handler will only execute once.

@@ -288,3 +284,3 @@ ```js

```
#### RegExp queries
## RegExp queries
Strings and RegExp expressions are interchangable.

@@ -297,3 +293,3 @@ ```js

```
#### Expressions
## Expressions
Qbus supports three modifiers:

@@ -320,3 +316,3 @@ - /:capture

```
#### Wildcards *
### Wildcards *
Matches everything at the position of the wildcard if the preceding and subsequent criteria is met. I.e.

@@ -323,0 +319,0 @@ ```js

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc