basic get&set of Map properties works

This commit is contained in:
Kevin Jahns 2015-06-29 13:20:19 +02:00
parent 8f63147dbc
commit bffbb6ca27
14 changed files with 146 additions and 56 deletions

View File

@ -53,9 +53,9 @@ var polyfills = [
];
var files = {
y: polyfills.concat(["src/**/*.js", "!src/**/*.spec.js"]),
y: polyfills.concat(["src/y.js", "src/**/*.js", "!src/**/*.spec.js"]),
lint: ["src/**/*.js", "gulpfile.js"],
test: polyfills.concat(["src/**/*.js"]),
test: polyfills.concat(["src/y.js", "src/**/*.js"]),
build_test: ["build_test/y.js"]
};
@ -119,7 +119,7 @@ gulp.task("build_jasmine_browser", function(){
});
gulp.task("develop", ["build_jasmine_browser", "test", "build"], function(){
gulp.task("develop", ["build_jasmine_browser", "build"], function(){
gulp.watch(files.test, ["build_jasmine_browser"]);
gulp.watch(files.test, ["test"]);

View File

@ -5,7 +5,8 @@ class AbstractConnector { //eslint-disable-line no-unused-vars
.role : String Role of this client ("master" or "slave")
.userId : String that uniquely defines the user.
*/
constructor (opts) {
constructor (y, opts) {
this.y = y;
if (opts == null){
opts = {};
}
@ -23,7 +24,8 @@ class AbstractConnector { //eslint-disable-line no-unused-vars
this.currentSyncTarget = null;
}
setUserId (userId) {
this.os.setUserId(userId);
this.userId = userId;
this.y.db.setUserId(userId);
}
onUserEvent (f) {
this.userEventListeners.push(f);

View File

@ -16,8 +16,9 @@ var globalRoom = {
users: {},
buffers: {},
removeUser: function(user){
for (var u of this.users) {
u.userLeft(user);
for (var i in this.users) {
this.users[i].userLeft(user);
}
delete this.users[user];
delete this.buffers[user];
@ -49,11 +50,11 @@ setInterval(function(){
var userIdCounter = 0;
class Test extends AbstractConnector {
constructor (options) {
constructor (y, options) {
if(options === undefined){
throw new Error("Options must not be undefined!");
}
super({
super(y, {
role: "master"
});

View File

@ -5,10 +5,11 @@ class AbstractTransaction { //eslint-disable-line no-unused-vars
}
// returns false if operation is not expected.
*addOperation (op) {
var state = this.getState(op.id[0]);
var state = yield* this.getState(op.id[0]);
if (op.id[1] === state.clock){
state.clock++;
yield* this.setState(state);
yield* this.setOperation(op);
this.store.operationAdded(op);
return true;
} else {
@ -16,6 +17,7 @@ class AbstractTransaction { //eslint-disable-line no-unused-vars
}
}
}
Y.AbstractTransaction = AbstractTransaction;
type Listener = {
f : GeneratorFunction, // is called when all operations are available
@ -47,6 +49,9 @@ class AbstractOperationStore { //eslint-disable-line no-unused-vars
a property before you iterate over it!
*/
}
setUserId (userId) {
this.userId = userId;
}
apply (ops) {
for (var o of ops) {
var required = Y.Struct[o.type].requiredOps(o);
@ -169,3 +174,4 @@ class AbstractOperationStore { //eslint-disable-line no-unused-vars
ls.push(f);
}
}
Y.AbstractOperationStore = AbstractOperationStore;

View File

@ -113,11 +113,16 @@ Y.IndexedDB = (function(){ //eslint-disable-line no-unused-vars
if (opts == null) {
opts = {};
}
if (opts.namespace != null || typeof opts.namespace !== "string") {
if (opts.namespace == null || typeof opts.namespace !== "string") {
throw new Error("IndexedDB: expect a string (opts.namespace)!");
} else {
this.namespace = opts.namespace;
}
if (opts.idbVersion != null) {
this.idbVersion = opts.idbVersion;
} else {
this.idbVersion = 5;
}
this.transactionQueue = {
queue: [],
@ -127,7 +132,7 @@ Y.IndexedDB = (function(){ //eslint-disable-line no-unused-vars
var store = this;
var tGen = (function *transactionGen(){
store.db = yield indexedDB.open(opts.namespace, 3);
store.db = yield indexedDB.open(opts.namespace, store.idbVersion);
var transactionQueue = store.transactionQueue;
var transaction = null;
@ -174,8 +179,12 @@ Y.IndexedDB = (function(){ //eslint-disable-line no-unused-vars
};
request.onupgradeneeded = function(event){
var db = event.target.result;
db.createObjectStore("OperationStore", {keyPath: "id"});
db.createObjectStore("StateVector", {keyPath: "user"});
try {
db.createObjectStore("OperationStore", {keyPath: "id"});
db.createObjectStore("StateVector", {keyPath: "user"});
} catch (e) {
// console.log("Store already exists!");
}
};
} else {
tGen.throw("You can not yield this type!");

View File

@ -4,7 +4,7 @@
if(typeof window !== "undefined"){
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
describe("IndexedDB", function() {
var ob = new IndexedDB("Test");
var ob = new Y.IndexedDB(null, {namespace: "Test"});
it("can add and get operation", function(done) {
ob.requestTransaction(function*(){

View File

@ -16,7 +16,7 @@ Y.Memory = (function(){ //eslint-disable-line no-unused-vars
constructor (store : OperationStore) {
super(store);
this.sv = store.ss;
this.ss = store.ss;
this.os = store.os;
}
*setOperation (op) {
@ -30,7 +30,7 @@ Y.Memory = (function(){ //eslint-disable-line no-unused-vars
delete this.os[JSON.stringify(id)];
}
*setState (state : State) : State {
this.sv[state.user] = state.clock;
this.ss[state.user] = state.clock;
}
*getState (user : string) : State {
var clock = this.ss[user];
@ -80,18 +80,21 @@ Y.Memory = (function(){ //eslint-disable-line no-unused-vars
}
}
class OperationStore extends AbstractOperationStore { //eslint-disable-line no-undef
namespace: string;
ready: Promise;
whenReadyListeners: Array<Function>;
constructor (y) {
super(y);
this.os = {};
this.ss = {};
}
requestTransaction (makeGen : Function) {
var t = new Transaction(this);
var gen = makeGen.call(t);
gen.next();
if (gen.done !== true) {
throw new Error("transaction is supposed to be done. Note: you may not yield with this transaction! (yield* is allowed though)");
var res = gen.next();
while(!res.done){
if (res.value === "transaction") {
res = gen.next(t);
} else {
throw new Error("You may not yield this type. (Maybe you meant to use 'yield*'?)");
}
}
}
*removeDatabase () {

View File

@ -19,6 +19,21 @@ type Insert = {
content: any
};
function compareIds(id1, id2) {
if (id1 == null) {
if (id2 == null) {
return true;
} else {
return false;
}
}
if (id1[0] === id2[0] && id1[1] === id2[1]) {
return true;
} else {
return false;
}
}
var Struct = {
Operation: { //eslint-disable-line no-unused-vars
create: function*(op : Op) : Struct.Operation {
@ -55,6 +70,25 @@ var Struct = {
op.right.left = op.id;
yield* this.setOperation(op.right);
}
var parent = yield* this.getOperation(op.parent);
if (op.parentSub != null){
if (compareIds(parent.map[op.parentSub], op.left)) {
parent.map[op.parentSub] = op.id;
yield* this.setOperation(parent);
}
} else {
var start = compareIds(parent.start, op.right);
var end = compareIds(parent.end, op.left);
if (start || end) {
if (start) {
parent.start = op.id;
}
if (end) {
parent.end = op.id;
}
yield* this.setOperation(parent);
}
}
return op;
},
requiredOps: function(op, ids){
@ -209,6 +243,11 @@ var Struct = {
}
},
Map: {
/*
{
// empty
}
*/
create: function*( op : Op ){
op.map = {};
op.struct = "Map";
@ -224,7 +263,7 @@ var Struct = {
// nop
},
get: function* (op, name) {
return yield* this.getOperation(op.map[name].end);
return (yield* this.getOperation(op.map[name])).content;
},
set: function* (op, name, value) {
var end = op.map[name];
@ -243,3 +282,5 @@ var Struct = {
}
}
};
Y.Struct = Struct;

29
src/Types/Map.js Normal file
View File

@ -0,0 +1,29 @@
(function(){
class Map {
constructor (_model) {
this._model = _model;
}
*val () {
var transaction = yield "transaction";
var model = yield* transaction.getOperation(this._model);
if (arguments.length === 0) {
throw new Error("Implement this case!");
} else if (arguments.length === 1) {
return yield* Y.Struct.Map.get.call(transaction, model, arguments[0]);
} else {
return yield* Y.Struct.Map.set.call(transaction, model, arguments[0], arguments[1]);
}
}
}
Y.Map = function* YMap(){
if (this instanceof Y.AbstractOperationStore) {
var model = yield* Y.Struct.map.create.call(this);
return new Map(model);
} else {
throw new Error("Don't use `new` to create this type!");
}
};
Y.Map.Create = Map;
})();

View File

@ -1,25 +0,0 @@
(function(){
class Map {
constructor (_model) {
this._model = _model;
}
*val () {
var transaction = yield "transaction";
var model = transaction.getOperation(this._model);
if (arguments.length === 0) {
throw new Error("Implement this case!");
} else if (arguments.length === 1) {
return yield* this.Struct.Map.get.call(transaction, model, arguments[0]);
} else {
return yield* this.Struct.Map.set.call(transaction, model, arguments[0], arguments[1]);
}
}
}
Y.Map = function* YMap(){
var model = yield* this.Struct.map.create.call(this);
return new Map(model);
};
Y.Map.Create = Map;
})();

View File

@ -1,11 +1,25 @@
/* @flow */
const GeneratorFunction = (function*(){}).constructor;
class Y { //eslint-disable-line no-unused-vars
constructor (opts) {
this.connector = new Y[opts.connector.name](opts.connector);
this.db = new Y[opts.db.name](this, opts.db);
this.connector = new Y[opts.connector.name](this, opts.connector);
var y = this;
this.db.requestTransaction(function*(){
yield* this.addOperation({
id: ["_", 0],
struct: "Map",
map: {}
});
y.root = new Y.Map.Create(["_", 0]);
});
}
transact (generator) {
if (generator.constructor !== GeneratorFunction) {
throw new Error("y.transact requires a Generator function! E.g. function*(){/*..*/}");
}
this.db.requestTransaction(generator);
}
destroy () {

View File

@ -2,6 +2,7 @@
/*eslint-env browser,jasmine */
describe("Yjs (basic)", function(){
jasmine.DEFAULT_TIMEOUT_INTERVAL = 500;
beforeEach(function(){
this.users = [];
for (var i = 0; i < 5; i++) {
@ -21,9 +22,18 @@ describe("Yjs (basic)", function(){
}
this.users = [];
});
it("can List.insert and get value from the other user", function(done){
this.users[0].val("name", 1);
this.users[0].connector.whenSynced(function(){
it("There is an initial Map type", function(done){
var y = this.users[0];
y.transact(function*(){
expect(y.root).not.toBeUndefined();
done();
});
});
it("Basic get&set of Map property", function(done){
var y = this.users[0];
y.transact(function*(){
yield* y.root.val("stuff", "stuffy");
expect(yield* y.root.val("stuff")).toEqual("stuffy");
done();
});
});

4
y.js

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long