Home

Awesome

qlobber   ci Coverage Status NPM version

Node.js globbing for amqp-like topics.

Note: Version 5.0.0 adds async and worker thread support when used on Node 12+.

Example:

var assert = require('assert');
var Qlobber = require('qlobber').Qlobber;
var matcher = new Qlobber();
matcher.add('foo.*', 'it matched!');
assert.deepEqual(matcher.match('foo.bar'), ['it matched!']);
assert(matcher.test('foo.bar', 'it matched!'));

The API is described here.

qlobber is implemented using a trie, as described in the RabbitMQ blog posts here and here.

Installation

npm install qlobber

Another Example

A more advanced example using topics from the RabbitMQ topic tutorial:

var assert = require('assert');
var Qlobber = require('qlobber').Qlobber;
var matcher = new Qlobber();
matcher.add('*.orange.*', 'Q1');
matcher.add('*.*.rabbit', 'Q2');
matcher.add('lazy.#', 'Q2');
assert.deepEqual(['quick.orange.rabbit',
                  'lazy.orange.elephant',
                  'quick.orange.fox',
                  'lazy.brown.fox',
                  'lazy.pink.rabbit',
                  'quick.brown.fox',
                  'orange',
                  'quick.orange.male.rabbit',
                  'lazy.orange.male.rabbit'].map(function (topic)
                  {
                      return matcher.match(topic).sort();
                  }),
                 [['Q1', 'Q2'],
                  ['Q1', 'Q2'],
                  ['Q1'],
                  ['Q2'],
                  ['Q2', 'Q2'],
                  [],
                  [],
                  [],
                  ['Q2']]);

Async Example

Same as the first example but using await:

const assert = require('assert');
const { Qlobber } = require('qlobber').set_native(require('qlobber-native'));
const matcher = new Qlobber.nativeString();

(async () => {
    await matcher.addP('foo.*', 'it matched!');
    assert.deepEqual(await matcher.matchP('foo.bar'), ['it matched!']);
    assert(await matcher.testP('foo.bar', 'it matched!'));
})();

Worker Thread Example

Same again but the matching is done on a separate thread:

const { Qlobber } = require('qlobber').set_native(require('qlobber-native'));
const {
    Worker, isMainThread, parentPort, workerData
} = require('worker_threads');

if (isMainThread) {
    const matcher = new Qlobber.nativeString();
    matcher.add('foo.*', 'it matched!');
    const worker = new Worker(__filename, {
        workerData: matcher.state_address
    });
    worker.on('message', msg => {
        const assert = require('assert');
        assert.deepEqual(msg, [['it matched!'], true]);
    });
} else {
    const matcher = new Qlobber.nativeString(workerData);
    parentPort.postMessage([
        matcher.match('foo.bar'),
        matcher.test('foo.bar', 'it matched!')
    ]);
}

Licence

MIT

Tests

qlobber passes the RabbitMQ topic tests (I converted them from Erlang to Javascript).

To run the tests:

npm test

Lint

grunt lint

Code Coverage

npm run coverage

c8 results are available here.

Coveralls page is here.

Benchmarks

grunt bench

qlobber is also benchmarked in ascoltatori.

Native Qlobbers

The Javascript Qlobbers don't support asynchronous calls and worker threads because Javascript values can't be shared between threads.

In order to support asynchronous calls and worker threads, a native C++ implementation is available in the qlobber-native module.

Add qlobber-native as a dependency to your project and then add it to qlobber like this:

require('qlobber').set_native(require('qlobber-native'));

Note that set_native returns qlobber's exports so you can do something like this:

const { Qlobber } = require('qlobber').set_native(require('qlobber-native'));

Note that qlobber-native requires Gnu C++ version 9+ and Boost 1.70+, including the boost_context runtime library.

Once's you've added it to qlobber, the following classes will be available alongside the Javascript classes:

They can only hold values of a single type (currently strings or numbers).

Asynchronous calls

The native classes support the same API as the Javascript classes but have the following additional methods:

They correspond to their namesakes but return Promises. Note that match_iterP and visitP return async iterators.

API

Source: lib/qlobber.js

<a name="tableofcontents"></a>

Qlobber([options])

Creates a new qlobber.

Parameters:

<sub>Go: TOC</sub>

<a name="qlobberprototype"></a>

Qlobber.prototype.add(topic, val)

Add a topic matcher to the qlobber.

Note you can match more than one value against a topic by calling add multiple times with the same topic and different values.

Parameters:

Return:

{Qlobber} The qlobber (for chaining).

<sub>Go: TOC | Qlobber.prototype</sub>

Qlobber.prototype.remove(topic, [val])

Remove a topic matcher from the qlobber.

Parameters:

Return:

{Qlobber} The qlobber (for chaining).

<sub>Go: TOC | Qlobber.prototype</sub>

Qlobber.prototype.match(topic)

Match a topic.

Parameters:

Return:

{Array} List of values that matched the topic. This may contain duplicates. Use a QlobberDedup if you don't want duplicates.

<sub>Go: TOC | Qlobber.prototype</sub>

Qlobber.prototype.match_iter()

Match a topic, returning the matches one at a time.

Return:

{Iterator} An iterator on the values that match the topic. There may be duplicate values, even if you use a QlobberDedup.

<sub>Go: TOC | Qlobber.prototype</sub>

Qlobber.prototype.test(topic, val)

Test whether a topic match contains a value. Faster than calling match and searching the result for the value. Values are tested using test_values.

Parameters:

Return:

{Boolean} Whether matching against topic contains val.

<sub>Go: TOC | Qlobber.prototype</sub>

Qlobber.prototype.test_values(vals, val)

Test whether values found in a match contain a value passed to test. You can override this to provide a custom implementation. Defaults to using indexOf.

Parameters:

Return:

{Boolean} Whether vals contains val.

<sub>Go: TOC | Qlobber.prototype</sub>

Qlobber.prototype.clear()

Reset the qlobber.

Removes all topic matchers from the qlobber.

Return:

{Qlobber} The qlobber (for chaining).

<sub>Go: TOC | Qlobber.prototype</sub>

Qlobber.prototype.visit()

Visit each node in the qlobber's trie in turn.

Return:

{Iterator} An iterator on the trie. The iterator returns objects which, if fed (in the same order) to the function returned by get_restorer on a different qlobber, will build that qlobber's trie to the same state. The objects can be serialized using JSON.stringify, if the values you store in the qlobber are also serializable.

<sub>Go: TOC | Qlobber.prototype</sub>

Qlobber.prototype.get_restorer([options])

Get a function which can restore the qlobber's trie to a state you retrieved by calling visit on this or another qlobber.

Parameters:

Return:

{Function} Function to call in order to rebuild the qlobber's trie. You should call this repeatedly with the objects you received from a call to visit. If you serialized the objects, remember to deserialize them first (e.g. with JSON.parse)!

<sub>Go: TOC | Qlobber.prototype</sub>

QlobberDedup([options])

Creates a new de-duplicating qlobber.

Inherits from Qlobber.

Parameters:

<sub>Go: TOC</sub>

<a name="qlobberdedupprototype"></a>

QlobberDedup.prototype.test_values(vals, val)

Test whether values found in a match contain a value passed to test. You can override this to provide a custom implementation. Defaults to using has.

Parameters:

Return:

{Boolean} Whether vals contains val.

<sub>Go: TOC | QlobberDedup.prototype</sub>

QlobberDedup.prototype.match(topic)

Match a topic.

Parameters:

Return:

{Set} ES6 Set of values that matched the topic.

<sub>Go: TOC | QlobberDedup.prototype</sub>

QlobberTrue([options])

Creates a new qlobber which only stores the value true.

Whatever value you add to this qlobber (even undefined), a single, de-duplicated true will be stored. Use this qlobber if you only need to test whether topics match, not about the values they match to.

Inherits from Qlobber.

Parameters:

<sub>Go: TOC</sub>

<a name="qlobbertrueprototype"></a>

QlobberTrue.prototype.test_values()

This override of test_values always returns true. When you call test on a QlobberTrue instance, the value you pass is ignored since it only cares whether a topic is matched.

Return:

{Boolean} Always true.

<sub>Go: TOC | QlobberTrue.prototype</sub>

QlobberTrue.prototype.match(topic)

Match a topic.

Since QlobberTrue only cares whether a topic is matched and not about values it matches to, this override of match just calls test (with value undefined).

Parameters:

Return:

{Boolean} Whether the QlobberTrue instance matches the topic.

<sub>Go: TOC | QlobberTrue.prototype</sub>

VisitorStream(qlobber)

Creates a new Readable stream, in object mode, which calls visit on a qlobber to generate its data.

You could pipe this to a JSONStream.stringify stream, for instance, to serialize the qlobber to JSON. See this test for an example.

Inherits from Readable.

Parameters:

<sub>Go: TOC</sub>

RestorerStream(qlobber)

Creates a new Writable stream, in object mode, which passes data written to it into the function returned by calling get_restorer on a qlobber.

You could pipe a JSONStream.parse stream to this, for instance, to deserialize the qlobber from JSON. See this test for an example.

Inherits from Writable.

Parameters:

<sub>Go: TOC</sub>

set_native(qlobber_native)

Add qlobber-native to qlobber.

Parameters:

Return:

{Object} The qlobber exports with the following native classes added:

<sub>Go: TOC</sub>

—generated by apidox