updated whenOperationExists

This commit is contained in:
Kevin Jahns
2015-06-21 03:50:58 +02:00
parent dcec0fe967
commit b3e09d001f
7 changed files with 31 additions and 15 deletions

View File

@@ -0,0 +1,175 @@
type State = {
user: string,
clock: number
};
type StateVector = Array<State>;
type StateSet = Object;
type IDBTransaction = Function;
type IDBObjectStore = Function;
type IDBRequest = Function;
type IDBCursor = Function;
type IDBKeyRange = Function;
type IDBOpenDBRequest = Function;
declare var indexedDB : Object;
declare var setTimeout : Function;
var IndexedDB = (function(){ //eslint-disable-line no-unused-vars
class Transaction extends AbstractTransaction { //eslint-disable-line
transaction: IDBTransaction;
sv: IDBObjectStore;
os: IDBObjectStore;
store: OperationStore;
constructor (store : OperationStore) {
super(store);
this.transaction = store.db.transaction(["OperationStore", "StateVector"], "readwrite");
this.sv = this.transaction.objectStore("StateVector");
this.os = this.transaction.objectStore("OperationStore");
this.buffer = {};
}
*setOperation (op) {
yield this.os.put(op);
this.buffer[JSON.stringify(op.uid)] = op;
return op;
}
*getOperation (id) {
var op = this.buffer[JSON.stringify(id)];
if (op == null) {
op = yield this.os.get(id);
this.buffer[JSON.stringify(id)] = op;
}
return op;
}
*removeOperation (id) {
this.buffer[JSON.stringify(id)] = null;
return yield this.os.delete(id);
}
*setState (state : State) : State {
return yield this.sv.put(state);
}
*getState (user : string) : State {
var state;
if ((state = yield this.sv.get(user)) != null){
return state;
} else {
return {
user: user,
clock: 0
};
}
}
*getStateVector () : StateVector {
var stateVector = [];
var cursorResult = this.sv.openCursor();
var cursor;
while ((cursor = yield cursorResult) != null) {
stateVector.push(cursor.value);
cursor.continue();
}
return stateVector;
}
*getStateSet () : StateSet {
var sv : StateVector = yield* this.getStateVector();
var ss : StateSet = {};
for (var state of sv){
ss[state.user] = state.clock;
}
return ss;
}
*getOperations (startSS : StateSet) {
if (startSS == null){
startSS = {};
}
var ops = [];
var endSV : StateVector = yield* this.getStateVector();
for (var endState of endSV) {
var user = endState.user;
var startPos = startSS[user] || 0;
var endPos = endState.clock;
var range = IDBKeyRange.bound([user, startPos], [user, endPos]);
var cursorResult = this.os.openCursor(range);
var cursor;
while ((cursor = yield cursorResult) != null) {
ops.push(cursor.value);
cursor.continue();
}
}
return ops;
}
}
class OperationStore extends AbstractOperationStore { //eslint-disable-line no-undef
namespace: string;
ready: Promise;
whenReadyListeners: Array<Function>;
constructor (namespace : string) {
super();
this.whenReadyListeners = [];
this.namespace = namespace;
this.ready = false;
var req = indexedDB.open(namespace, 2); //eslint-disable-line no-undef
req.onerror = function(){
throw new Error("Couldn't open the IndexedDB database!");
};
req.onsuccess = (event)=>{
this.db = event.target.result;
this.whenReadyListeners.forEach(function(f){
setTimeout(f, 0);
});
this.whenReadyListeners = null;
this.ready = true;
};
req.onupgradeneeded = function(event){
var db = event.target.result;
db.createObjectStore("OperationStore", {keyPath: "id"});
db.createObjectStore("StateVector", {keyPath: "user"});
};
}
whenReady (f : Function) {
if (this.ready){
setTimeout(f, 0);
} else {
this.whenReadyListeners.push(f);
}
}
requestTransaction (makeGen : Function) {
this.whenReady(()=>{
var transaction = new Transaction(this);
var gen = makeGen.apply(transaction);
function handle(res : any){
var request : any = res.value;
if (res.done){
return;
} else if (request.constructor === IDBRequest
|| request.constructor === IDBCursor
|| request.constructor === IDBOpenDBRequest) {
request.onsuccess = function(){
handle(gen.next(request.result));
};
request.onerror = function(err){
gen.throw(err);
};
} else {
gen.throw("You can not yield this type!");
}
}
handle(gen.next());
});
}
*removeDatabase () {
this.db.close();
yield indexedDB.deleteDatabase(this.namespace);
}
}
return OperationStore;
})();

View File

@@ -0,0 +1,113 @@
/* @flow */
/*eslint-env browser,jasmine */
if(typeof window !== "undefined"){
describe("IndexedDB", function() {
var ob = new IndexedDB("Test");
it("can add and get operation", function(done) {
ob.requestTransaction(function*(){
var op = yield* this.setOperation({
"id": ["1", 0],
"stuff": true
});
expect(yield* this.getOperation(["1", 0]))
.toEqual(op);
done();
});
});
it("can remove operation", function(done) {
ob.requestTransaction(function*(){
var op = yield* this.setOperation({
"id": ["1", 0],
"stuff": true
});
expect(yield* this.getOperation(["1", 0]))
.toEqual(op);
yield* this.removeOperation(["1", 0]);
expect(yield* this.getOperation(["1", 0]))
.toBeUndefined();
done();
});
});
it("getOperation(op) returns undefined if op does not exist", function(done){
ob.requestTransaction(function*(){
var op = yield* this.getOperation("plzDon'tBeThere");
expect(op).toBeUndefined();
done();
});
});
it("yield throws if request is unknown", function(done){
ob.requestTransaction(function*(){
try {
yield this.getOperations(["u1", 0]);
} catch (e) {
expect(true).toEqual(true);
done();
return;
}
expect("Expected an Error!").toEqual(true);
done();
});
});
it("sets and gets stateVector", function(done){
ob.requestTransaction(function*(){
var s1 = {user: "1", clock: 1};
var s2 = {user: "2", clock: 3};
yield* this.setState(s1);
yield* this.setState(s2);
var sv = yield* this.getStateVector();
expect(sv).not.toBeUndefined();
expect(sv).toEqual([s1, s2]);
done();
});
});
it("gets stateSet", function(done){
ob.requestTransaction(function*(){
var s1 = {user: "1", clock: 1};
var s2 = {user: "2", clock: 3};
yield* this.setState(s1);
yield* this.setState(s2);
var sv = yield* this.getStateSet();
expect(sv).not.toBeUndefined();
expect(sv).toEqual({
"1": 1,
"2": 3
});
done();
});
});
it("getOperations returns operations (no parameters)", function(done){
ob.requestTransaction(function*(){
var s1 = {user: "1", clock: 55};
yield* this.setState(s1);
var op1 = yield* this.setOperation({
"id": ["1", 0],
"stuff": true
});
var op2 = yield* this.setOperation({
"id": ["1", 3],
"stuff": true
});
var ops = yield* this.getOperations();
expect(ops.length).toBeGreaterThan(1);
expect(ops[0]).toEqual(op1);
expect(ops[1]).toEqual(op2);
done();
});
});
afterAll(function(done){
ob.requestTransaction(function*(){
yield* ob.removeDatabase();
ob = null;
done();
});
});
});
}