diff --git a/docs/_layouts/learn.html b/docs/_layouts/learn.html
index 4f49ddc4f1..7379b1c4ca 100644
--- a/docs/_layouts/learn.html
+++ b/docs/_layouts/learn.html
@@ -31,7 +31,8 @@
API
Database Information
Compaction
Revision diff
- List databases
+ Database Events
+ Plugins
diff --git a/docs/api.md b/docs/api.md
index 86a7e3e7eb..5bf0e337e8 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -19,7 +19,7 @@ Additionally, any method that only returns a single thing (e.g. `db.get`, but no
new PouchDB([name], [options])
{% endhighlight %}
-This method creates a database or opens an existing one. If you use a URL like `http://domain.com/dbname` then PouchDB will work as a client to an online CouchDB instance. Otherwise it will create a local database using whatever backend is present (i.e. IndexedDB, WebSQL, or LevelDB).
+This method creates a database or opens an existing one. If you use a URL like `http://domain.com/dbname` then PouchDB will work as a client to an online CouchDB instance. Otherwise it will create a local database using whatever backend is present (i.e. IndexedDB, WebSQL, or LevelDB).
### Options
@@ -743,6 +743,19 @@ db.revsDiff({
}
{% endhighlight %}
+## Events
+
+PouchDB is an [event emiter](http://nodejs.org/api/events.html#events_class_events_eventemitter) and will emit a 'created' event when a database is created. A 'destroy' event is emited when a database is destroyed.
+
+{% highlight js %}
+PouchDB.on('created', function (dbName) {
+ // called whenver a db is created.
+});
+PouchDB.on('destroyed', function (dbName) {
+ // called whenver a db is destroyed.
+});
+{% endhighlight %}
+
## Plugins
Writing a plugin is easy the api is
@@ -753,4 +766,5 @@ PouchDB.plugin({
});
{% endhighlight %}
-This will add the function as a method of all databases with the given name, it will always be called in the so that `this` is db.
\ No newline at end of file
+This will add the function as a method of all databases with the given name, it will always be called in context so that `this` is db.
+
diff --git a/lib/adapter.js b/lib/adapter.js
index f787ba4af4..523e662a78 100644
--- a/lib/adapter.js
+++ b/lib/adapter.js
@@ -3,7 +3,7 @@
var utils = require('./utils');
var merge = require('./merge');
var errors = require('./deps/errors');
-
+var EventEmitter = require('events').EventEmitter;
/*
* A generic pouch adapter
*/
@@ -56,10 +56,11 @@ function computeHeight(revs) {
});
return height;
}
-
+utils.inherits(AbstractPouchDB, EventEmitter);
module.exports = AbstractPouchDB;
function AbstractPouchDB() {
var self = this;
+ EventEmitter.call(this);
self.autoCompact = function (callback) {
if (!self.auto_compaction) {
return callback;
@@ -86,6 +87,52 @@ function AbstractPouchDB() {
}
};
};
+ var listeners = 0, changes;
+ var eventNames = ['change', 'delete', 'create', 'update'];
+ this.on('newListener', function (eventName) {
+ if (~eventNames.indexOf(eventName)) {
+ if (listeners) {
+ listeners++;
+ return;
+ } else {
+ listeners++;
+ }
+ } else {
+ return;
+ }
+ var lastChange = 0;
+ changes = this.changes({
+ conflicts: true,
+ include_docs: true,
+ continuous: true,
+ since: 'latest',
+ onChange: function (change) {
+ if (change.seq <= lastChange) {
+ return;
+ }
+ lastChange = change.seq;
+ self.emit('change', change);
+ if (change.doc._deleted) {
+ self.emit('delete', change);
+ } else if (change.doc._rev.split('-')[0] === '1') {
+ self.emit('create', change);
+ } else {
+ self.emit('update', change);
+ }
+ }
+ });
+ });
+ this.on('removeListener', function (eventName) {
+ if (~eventNames.indexOf(eventName)) {
+ listeners--;
+ if (listeners) {
+ return;
+ }
+ } else {
+ return;
+ }
+ changes.cancel();
+ });
}
AbstractPouchDB.prototype.post = utils.toPromise(function (doc, opts, callback) {
if (typeof opts === 'function') {
diff --git a/lib/adapters/http.js b/lib/adapters/http.js
index f38edd278f..d55d21b633 100644
--- a/lib/adapters/http.js
+++ b/lib/adapters/http.js
@@ -1087,7 +1087,15 @@ function HttpPouch(opts, callback) {
utils.ajax({
url: genDBUrl(host, ''),
method: 'DELETE'
- }, callback);
+ }, function (err, resp) {
+ if (err) {
+ api.emit('error', err);
+ callback(err);
+ } else {
+ api.emit('destroyed');
+ callback(null, resp);
+ }
+ });
});
}
diff --git a/lib/adapters/leveldb.js b/lib/adapters/leveldb.js
index 40e5f918a9..79216b23ff 100644
--- a/lib/adapters/leveldb.js
+++ b/lib/adapters/leveldb.js
@@ -673,6 +673,10 @@ function LevelPouch(opts, callback) {
});
};
api.destroy = utils.toPromise(function (opts, callback) {
+ if (!this.taskqueue.isReady) {
+ this.taskqueue.addTask('destroy', arguments);
+ return;
+ }
if (typeof opts === 'function') {
callback = opts;
opts = {};
@@ -684,7 +688,15 @@ function LevelPouch(opts, callback) {
if (err) {
return callback(err);
}
- leveldown.destroy(name, callback);
+ leveldown.destroy(name, function (err, resp) {
+ if (err) {
+ api.emit('error', err);
+ callback(err);
+ } else {
+ api.emit('destroyed');
+ callback(null, resp);
+ }
+ });
});
}
});
diff --git a/lib/adapters/websql.js b/lib/adapters/websql.js
index 69a1c56904..55c27ba61c 100644
--- a/lib/adapters/websql.js
+++ b/lib/adapters/websql.js
@@ -892,6 +892,7 @@ WebSqlPouch.valid = function () {
};
WebSqlPouch.destroy = utils.toPromise(function (name, opts, callback) {
+ var self = this;
var db = openDB(name, POUCH_VERSION, name, POUCH_SIZE);
db.transaction(function (tx) {
tx.executeSql('DROP TABLE IF EXISTS ' + DOC_STORE, []);
@@ -899,6 +900,7 @@ WebSqlPouch.destroy = utils.toPromise(function (name, opts, callback) {
tx.executeSql('DROP TABLE IF EXISTS ' + ATTACH_STORE, []);
tx.executeSql('DROP TABLE IF EXISTS ' + META_STORE, []);
}, unknownError(callback), function () {
+ self.emit('destroyed');
callback();
});
});
diff --git a/lib/constructor.js b/lib/constructor.js
index 26f83854fa..174ab11d8a 100644
--- a/lib/constructor.js
+++ b/lib/constructor.js
@@ -133,6 +133,15 @@ function PouchDB(name, opts, callback) {
}
return;
}
+ function destructionListner(event) {
+ if (event === 'destroyed') {
+ self.emit('destroyed');
+ PouchDB.removeListener(opts.name, destructionListner);
+ }
+ }
+ PouchDB.on(opts.name, destructionListner);
+ self.emit('created', self);
+ PouchDB.emit('created', opts.originalName);
self.taskqueue.ready(self);
callback(null, self);
diff --git a/lib/setup.js b/lib/setup.js
index 39d84816bc..b4757bf404 100644
--- a/lib/setup.js
+++ b/lib/setup.js
@@ -2,10 +2,28 @@
var PouchDB = require("./constructor");
var utils = require('./utils');
+var EventEmitter = require('events').EventEmitter;
PouchDB.adapters = {};
PouchDB.prefix = '_pouch_';
+var eventEmitter = new EventEmitter();
+
+var eventEmitterMethods = [
+ 'on',
+ 'addListener',
+ 'emit',
+ 'listeners',
+ 'once',
+ 'removeAllListeners',
+ 'removeListener',
+ 'setMaxListeners'
+];
+
+eventEmitterMethods.forEach(function (method) {
+ PouchDB[method] = eventEmitter[method].bind(eventEmitter);
+});
+PouchDB.setMaxListeners(0);
PouchDB.parseAdapter = function (name) {
var match = name.match(/([a-z\-]*):\/\/(.*)/);
var adapter;
@@ -50,7 +68,16 @@ PouchDB.destroy = utils.toPromise(function (name, opts, callback) {
var dbName = backend.name;
// call destroy method of the particular adaptor
- PouchDB.adapters[backend.adapter].destroy(dbName, opts, callback);
+ PouchDB.adapters[backend.adapter].destroy(dbName, opts, function (err, resp) {
+ if (err) {
+ callback(err);
+ } else {
+ PouchDB.emit('destroyed', dbName);
+ //so we don't have to sift through all dbnames
+ PouchDB.emit(dbName, 'destroyed');
+ callback(null, resp);
+ }
+ });
});
PouchDB.allDbs = utils.toPromise(function (callback) {
var err = new Error('allDbs method removed');
diff --git a/package.json b/package.json
index 8b52e5a11e..82acae018d 100644
--- a/package.json
+++ b/package.json
@@ -61,7 +61,6 @@
"request": false,
"levelup": false,
"leveldown": false,
- "events": false,
"crypto": false,
"bluebird": "lie",
"level-sublevel": false
diff --git a/tests/test.events.js b/tests/test.events.js
new file mode 100644
index 0000000000..0e11dfe85e
--- /dev/null
+++ b/tests/test.events.js
@@ -0,0 +1,118 @@
+'use strict';
+
+var adapters = [
+ 'http-1',
+ 'local-1'
+];
+adapters.forEach(function (adapter) {
+ describe('test.events.js-' + adapter, function () {
+ //we can't use the same db becasue
+ var i = 0;
+ var dbs = {};
+ beforeEach(function () {
+ dbs.name = testUtils.adapterUrl(adapter, 'events_tests' + i++);
+ });
+
+ afterEach(function (done) {
+ PouchDB.destroy(dbs.name, done);
+ });
+
+ it('PouchDB emits creation event', function (done) {
+ PouchDB.once('created', function (name) {
+ name.should.equal(dbs.name, 'should be same thing');
+ done();
+ });
+ new PouchDB(dbs.name);
+ });
+ it('PouchDB emits destruction event', function (done) {
+ new PouchDB(dbs.name + 1, function () {
+ PouchDB.destroy(dbs.name + 1);
+ }).once('destroyed', function () {
+ new PouchDB(dbs.name, function () {
+ done();
+ });
+ });
+ });
+ it('emit creation event', function (done) {
+ var db = new PouchDB(dbs.name).on('created', function (newDB) {
+ db.should.equal(newDB, 'should be same thing');
+ done();
+ });
+ });
+
+ it('emit changes event', function (done) {
+ new PouchDB(dbs.name, function (err, db) {
+ var id = 'emiting';
+ var obj = {
+ something: 'here',
+ somethingElse: 'overHere'
+ };
+ db.on('change', function (change) {
+ change.seq.should.equal(1, 'changed');
+ change.id.should.equal('emiting');
+ done(err);
+ });
+ db.put(obj, id);
+ });
+ });
+
+ it('emit create event', function (done) {
+ new PouchDB(dbs.name, function (err, db) {
+ var id = 'creating';
+ var obj = {
+ something: 'here',
+ somethingElse: 'overHere'
+ };
+ db.on('create', function (change) {
+ change.id.should.equal('creating');
+ change.seq.should.equal(1, 'created');
+ done(err);
+ });
+ db.put(obj, id);
+ });
+ });
+
+ it('emit update event', function (done) {
+ new PouchDB(dbs.name, function (err, db) {
+ var id = 'updating';
+ var obj = {
+ something: 'here',
+ somethingElse: 'overHere'
+ };
+ db.on('update', function (change) {
+ change.id.should.equal('updating');
+ change.seq.should.equal(2, 'seq 2, updated');
+ done(err);
+ });
+
+ db.put(obj, id).then(function (doc) {
+ db.put({'something': 'else'}, id, doc.rev);
+ });
+
+ });
+ });
+
+ it('emit delete event', function (done) {
+ new PouchDB(dbs.name, function (err, db) {
+ var id = 'emiting';
+ var obj = {
+ something: 'here',
+ somethingElse: 'overHere'
+ };
+ db.on('delete', function (change) {
+ change.seq.should.equal(2, 'deleted');
+ change.id.should.equal('emiting');
+ done(err);
+ });
+
+ db.put(obj, id).then(function (doc) {
+ db.remove({
+ _id: id,
+ _rev: doc.rev
+ });
+ });
+ });
+ });
+
+ });
+});
\ No newline at end of file
diff --git a/tests/test.html b/tests/test.html
index 25901f40c7..96856d3e41 100644
--- a/tests/test.html
+++ b/tests/test.html
@@ -29,6 +29,7 @@
+