Skip to content

Commit

Permalink
Merge branch 'brettz9-server-handlers'
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronpowell committed Mar 9, 2016
2 parents 1f3e51d + afbb37d commit 9a51b55
Show file tree
Hide file tree
Showing 20 changed files with 707 additions and 614 deletions.
51 changes: 41 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,19 +91,19 @@ db.open({
throw err;
}).then(function (s) {
server = s;
// One can add a versionchange handler here to self-close
// the connection upon future upgrade attempts (likely to
// be one made in other tabs) and thereby
// avoid such attempts having to face blocking errors.
});
```

For cases where the blocking connections are in other tabs/windows,
[window.postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window.postMessage)
might be a suitable way to communicate with them to get them to close
their connections or whatever behavior is desired.

Check out the `/tests/specs` folder for more examples.

## General server/store methods

Note that by default the methods below can be called either as
Note that by default the methods below (not including `close`,
`addEventListener`, and `removeEventListener`) can be called either as
`server.people.xxx( arg1, arg2, ... )` or
`server.xxx( 'people', arg1, arg2, ... )`.

Expand Down Expand Up @@ -468,6 +468,37 @@ var db = server.getIndexedDB();
var storeNames = db.objectStoreNames;
```

## Server event handlers

All of the following are optional.

```js
server.addEventListener('abort', function (e) {
// Handle abort event
});
server.addEventListener('error', function (err) {
// Handle any errors (check err.name)
});
server.addEventListener('versionchange', function (e) {
// Be notified of version changes (can use e.oldVersion and e.newVersion)
});
```

All of the following shorter equivalent forms (which also work internally
via `addEventListener`) are optional and can be chained as desired.

```js
server.abort(function (e) {
// Handle abort event
}).error(function (err) {
// Handle any errors (check err.name)
}).versionchange(function (e) {
// Be notified of version changes (can use e.oldVersion and e.newVersion)
});
```

See the IndexedDB spec for the [possible exceptions](http://www.w3.org/TR/IndexedDB/#exceptions).

## Deleting a database

```js
Expand Down Expand Up @@ -510,15 +541,15 @@ db.cmp(key1, key2);

# Promise notes

db.js used the es6 Promise spec to handle asynchronous operations.
db.js used the ES6 Promise spec to handle asynchronous operations.

All operations that are asynchronous will return an instance of the
es6 Promise object that exposes a `then` method which will take up
ES6 Promise object that exposes a `then` method which will take up
to two callbacks, `onFulfilled` and `onRejected`. Please refer to
es6 promise spec for more information.
the ES6 Promise spec for more information.

As of version `0.7.0` db.js's Promise API is designed to work with
es6 Promises, please polyfill it if you would like to use other promise
ES6 Promises, please polyfill it if you would like to use another promise
library.

# Contributor notes
Expand Down
10 changes: 8 additions & 2 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ module.exports = function(config) {
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',

client: {
captureConsole: true,
mocha: {
bail: true
}
},

// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
Expand All @@ -15,7 +21,7 @@ module.exports = function(config) {

// list of files / patterns to load in the browser
files: [
'node_modules/es6-promise/dist/es6-promise.js',
'node_modules/babel-polyfill/dist/polyfill.js',
'dist/db.min.js',
'node_modules/chai/chai.js',
'tests/helpers/**/*.js',
Expand All @@ -25,7 +31,7 @@ module.exports = function(config) {

// list of files to exclude
exclude: [
// exclude the jQuery integraion tests for karma
// exclude the jQuery integration tests for karma
'tests/specs/thenable-integration.js',
// phantom doesn't support web workers
'tests/specs/web-workers.js'
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@
},
"dependencies": {},
"devDependencies": {
"babel-polyfill": "^6.6.1",
"babel-preset-es2015": "^6.0.15",
"body-parser": "1.x",
"chai": "^3.5.0",
"errorhandler": "1.x",
"es6-promise": "^3.1.2",
"eslint-config-standard": "^4.4.0",
"eslint-plugin-standard": "^1.3.1",
"express": "^4.13.4",
Expand Down
26 changes: 24 additions & 2 deletions src/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
}());

const dbCache = {};
const serverEvents = ['abort', 'error', 'versionchange'];

function mongoDBToKeyRangeArgs (opts) {
var keys = Object.keys(opts).sort();
Expand Down Expand Up @@ -232,6 +233,26 @@
});
};

this.addEventListener = function (eventName, handler) {
if (!serverEvents.includes(eventName)) {
throw new Error('Unrecognized event type ' + eventName);
}
db.addEventListener(eventName, handler);
};

this.removeEventListener = function (eventName, handler) {
if (!serverEvents.includes(eventName)) {
throw new Error('Unrecognized event type ' + eventName);
}
db.removeEventListener(eventName, handler);
};

serverEvents.forEach(function (evName) {
this[evName] = function (handler) {
this.addEventListener(evName, handler);
};
}, this);

if (noServerMethods) {
return;
}
Expand All @@ -245,7 +266,7 @@
}
this[storeName] = {};
var keys = Object.keys(this);
keys.filter(key => key !== 'close')
keys.filter(key => !(([...serverEvents, 'close', 'addEventListener', 'removeEventListener']).includes(key)))
.map(key =>
this[storeName][key] = (...args) => this[key](storeName, ...args)
);
Expand Down Expand Up @@ -488,7 +509,7 @@

for (var i = 0; i < db.objectStoreNames.length; i++) {
var name = db.objectStoreNames[i];
if (schema.hasOwnProperty(name) === false) {
if (!schema.hasOwnProperty(name)) {
e.currentTarget.transaction.db.deleteObjectStore(name);
}
}
Expand Down Expand Up @@ -579,6 +600,7 @@
// the user unblocks by closing the blocking
// connection
request.onsuccess = ev => {
// Attempt to workaround Firefox event version problem: https://bugzilla.mozilla.org/show_bug.cgi?id=1220279
if (!('newVersion' in ev)) {
ev.newVersion = e.newVersion;
}
Expand Down
86 changes: 43 additions & 43 deletions tests/specs/blocked-events.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,57 +21,57 @@
};

beforeEach(function (done) {
this.dbName = guid();
var spec = this;
this.dbName = guid();
db.open({
server: this.dbName,
version: initialVersion,
schema: schema
}).then(function (s) {
spec.server = s;
}).then(function () {
spec.item1 = {
firstName: 'Aaron',
lastName: 'Powell',
age: 20
};
spec.item2 = {
firstName: 'John',
lastName: 'Smith',
age: 30
};
spec.item3 = {
firstName: 'Aaron',
lastName: 'Jones',
age: 40,
specialID: 5
};
spec.server.add('test', spec.item1,
spec.item2, spec.item3).then(function () {
done();
}
);
});
});

afterEach(function (done) {
// We close the connection so that subsequent files are not blocked
if (this.server && !this.server.isClosed()) {
this.server.close();
}
this.server = undefined;

var req = indexedDB.deleteDatabase(this.dbName);

req.onsuccess = function () {
db.open({
server: spec.dbName,
version: initialVersion,
schema: schema
}).then(function (s) {
spec.server = s;
}).then(function () {
spec.item1 = {
firstName: 'Aaron',
lastName: 'Powell',
age: 20
};
spec.item2 = {
firstName: 'John',
lastName: 'Smith',
age: 30
};
spec.item3 = {
firstName: 'Aaron',
lastName: 'Jones',
age: 40,
specialID: 5
};
spec.server.add('test', spec.item1,
spec.item2, spec.item3).then(function () {
done();
}
);
});
done();
};

req.onerror = function () {
console.log('failed to delete db in beforeEach', arguments);
console.log('failed to delete db in afterEach', arguments);
};

req.onblocked = function () {
console.log('db blocked', arguments, spec);
console.log('db blocked', arguments);
};

});

afterEach(function (done) {
// We close the connection so that subsequent files are not blocked
if (!this.server.isClosed()) {
this.server.close();
}
done();
});

it('should receive blocked events (on db open) and be able to resume after unblocking', function (done) {
Expand Down
29 changes: 7 additions & 22 deletions tests/specs/close-db.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,30 @@
(function (db, describe, it, expect, beforeEach, afterEach) {
'use strict';
describe('db.close', function () {
var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.oIndexedDB || window.msIndexedDB;
var indexedDB = window.indexedDB || window.webkitIndexedDB ||
window.mozIndexedDB || window.oIndexedDB || window.msIndexedDB;

beforeEach(function (done) {
this.dbName = guid();
var req = indexedDB.deleteDatabase(this.dbName);

req.onsuccess = function () {
done();
};

req.onerror = function (e) {
console.log('error deleting db', arguments);
done(e);
};

req.onblocked = function (e) {
console.log('db blocked on delete', arguments);
done(e);
};
}, 10000);
done();
});

afterEach(function (done) {
if (this.server && !this.server.isClosed()) {
this.server.close();
}
this.server = undefined;

var req = indexedDB.deleteDatabase(this.dbName);

req.onsuccess = function (/* e */) {
req.onsuccess = function () {
done();
};

req.onerror = function (e) {
console.log('failed to delete db', arguments);
done(e);
};

req.onblocked = function (e) {
console.log('db blocked', arguments);
done(e);
};
});

Expand Down
Loading

0 comments on commit 9a51b55

Please sign in to comment.