PouchDB worker reacting to document changes.
Each document has a state. A clerk listens to changes in one or more PouchDB databases and react to document state changes.
When reacting to a document state change, the clerk can:
- change the document
- say what the next state is going to be
$ npm install pouch-clerk --save
var Clerk = require('pouch-clerk');
var transitions = {
// document defining state transition handlers
};
var options = {
initialState: 'start', // defaults to `start`
finalState: 'finished', // defaults to `finish`
reconnectMaxTimeoutMS: 3000, // defaults to 3000 ms
transitions: transitions,
};
var clerk = Clerk(options);
initialState
(string): the state to give new documents with no state yetfinalState
(string or string array): the end state (or states, if you pass an array)reconnectMaxTimeoutMS
(number): the maximum number of miliseconds to wait before trying to (re)connecttransitions
(object): the state transition handlers. Object where the keys are the state names and the values are functions.asyncUpdaters
: an array of functions that start and stop async updaters. See Async Updaters section.
The options.transitions
should contain an object with a key for each state.
The value for each state should be a handler function, like this:
var transitions = {
'start': function(doc, next) {
// ...
}
}
Example:
var transitions = {
'start': function(doc, next) {
somethingAsynchronous(function(err, result) {
if (err) {
doc.error(err);
} else {
doc.result = result;
next(null, 'waiting for driver'); // jump into next state
}
});
},
'waiting for driver': function(doc, next) {
// ...
}
You can force reentry on the same state (watch out for infinite loops), by passing a true
as third argument to the next
callback:
var transitions = {
'waiting for driver': function(doc, next) {
next(null, 'waiting for driver', true);
}
}
A clerk can react to one or more databases. During runtime you just add or remove a database:
var PouchDB = require('pouchdb');
var db = new PouchDB('somedatabase');
clerk.add(db);
clerk.remove(db);
You can also remove by database name:
clerk.add(db, 'mydb');
clerk.remove('mydb');
You can also find out if a named database exists:
if (!clerk.has('mydb')) {
clerk.add(db, 'mydb');
}
You can push async updates into documents like this:
var asyncUpdater = {
start: function(doc) {
this._interval = setInterval(function() {
doc.merge(function(currentDoc) {
currentDoc.counter = currentDoc.counter + 1;
})
}, 1000);
},
stop: function() {
clearInterval(this._interval);
}
}
var options = {
asyncUpdaters: [asyncUpdater]
};
var clerk = Clerk(options);
(Each document creates an async updater instance, which you can reference by this
inside the start
or stop
methods).
The Async Updater start
method gets a doc
instance for each document that is being handled. Using this document you can:
function start(doc) {
console.log(doc._id);
}
function start(doc) {
doc.get(function(err, latestDoc) {
// ...
})
}
function start(doc) {
doc.put(doc, function(err) {
});
}
function start(doc) {
doc.merge(function(latest) {
latest.counter ++;
}, function(err) {
// handle err
});
}
The error handling strategy depends on the type of error happening.
If an error occurs while you're processing a state transition, you should call the next
callback with that error as the first argument:
var transitions = {
'waiting for driver': function(doc, next) {
somethingAsynchronous(function(err) {
if (err) return next(err);
/// ...
});
}
};
This will make the document transition into the error
state.
You should define an error state handler (if you don't that error will be handled as an internal error — see the next section about internal errors).
var transitions = {
'error': function(err, doc, next) {
// you can recover from error:
next(null, 'some next state');
}
}
Internal errors can occurr when saving document changes. You can listen for those errors on the clerk object:
clerk.on('error', function(err) {
});
(if you don't, an uncaught exception will be thrown);
clerk.stop(callback);
ISC