fixing types.

This commit is contained in:
Kevin Jahns 2015-07-09 15:50:59 +02:00
parent f862fae473
commit 9b45a78e58
9 changed files with 76 additions and 61 deletions

View File

@ -4,8 +4,22 @@ class AbstractTransaction { //eslint-disable-line no-unused-vars
this.store = store; this.store = store;
} }
*getType (id) { *getType (id) {
var op = yield* this.getOperation(id); var sid = JSON.stringify(id);
return new Y[op.type].Create(op); var t = this.store.initializedTypes[sid];
if (t == null) {
var op = yield* this.getOperation(id);
if (op != null) {
t = new Y[op.type].Create(op.id);
this.store.initializedTypes[sid] = t;
}
}
return t;
}
createType (model) {
var sid = JSON.stringify(model.id);
var t = new Y[model.type].Create(model.id);
this.store.initializedTypes[sid] = t;
return t;
} }
// returns false if operation is not expected. // returns false if operation is not expected.
*addOperation (op) { *addOperation (op) {
@ -34,9 +48,6 @@ type Id = [string, number];
class AbstractOperationStore { //eslint-disable-line no-unused-vars class AbstractOperationStore { //eslint-disable-line no-unused-vars
constructor (y) { constructor (y) {
this.y = y; this.y = y;
this.parentListeners = {};
this.parentListenersRequestPending = false;
this.parentListenersActivated = {};
// E.g. this.listenersById[id] : Array<Listener> // E.g. this.listenersById[id] : Array<Listener>
this.listenersById = {}; this.listenersById = {};
// Execute the next time a transaction is requested // Execute the next time a transaction is requested
@ -53,6 +64,9 @@ class AbstractOperationStore { //eslint-disable-line no-unused-vars
Always remember to first overwrite Always remember to first overwrite
a property before you iterate over it! a property before you iterate over it!
*/ */
// TODO: Use ES7 Weak Maps. This way types that are no longer user,
// wont be kept in memory.
this.initializedTypes = {};
} }
setUserId (userId) { setUserId (userId) {
this.userId = userId; this.userId = userId;
@ -142,32 +156,11 @@ class AbstractOperationStore { //eslint-disable-line no-unused-vars
} }
} }
} }
// notify parent listeners, if possible // notify parent, if it has been initialized as a custom type
var listeners = this.parentListeners[op.parent]; var t = this.initializedTypes[JSON.stringify(op.parent)];
if ( this.parentListenersRequestPending if (t != null) {
|| ( listeners == null ) t._changed(op);
|| ( listeners.length === 0 )) {
return;
} }
var al = this.parentListenersActivated[JSON.stringify(op.parent)];
if ( al == null ){
al = [];
this.parentListenersActivated[JSON.stringify(op.parent)] = al;
}
al.push(op);
this.parentListenersRequestPending = true;
var store = this;
this.requestTransaction(function*(){
store.parentListenersRequestPending = false;
var activatedOperations = store.parentListenersActivated;
store.parentListenersActivated = {};
for (var parentId in activatedOperations){
var parent = yield* this.getOperation(parentId);
Struct[parent.struct].notifyObservers(activatedOperations[parentId]);
}
});
} }
removeParentListener (id, f) { removeParentListener (id, f) {
var ls = this.parentListeners[id]; var ls = this.parentListeners[id];

View File

@ -35,12 +35,10 @@ Y.Memory = (function(){ //eslint-disable-line no-unused-vars
return op; return op;
} }
*getOperation (id) { *getOperation (id) {
var op = this.os.find(id); if (id == null) {
if (op == null) { throw new Error("You must define id!");
throw new Error("Op does not exist..");
} else {
return op;
} }
return this.os.find(id);
} }
*removeOperation (id) { *removeOperation (id) {
this.os.delete(id); this.os.delete(id);
@ -123,13 +121,13 @@ Y.Memory = (function(){ //eslint-disable-line no-unused-vars
} }
requestTransaction (makeGen : Function) { requestTransaction (makeGen : Function) {
var t = new Transaction(this); var t = new Transaction(this);
var gen = makeGen.call(t, new Y.Map.Create(["_", 0])); var gen = makeGen.call(t, t.getType(["_", 0]).next().value);
var res = gen.next(); var res = gen.next();
while(!res.done){ while(!res.done){
if (res.value === "transaction") { if (res.value === "transaction") {
res = gen.next(t); res = gen.next(t);
} else { } else {
throw new Error("You may not yield this type. (Maybe you meant to use 'yield*'?)"); throw new Error("You must not yield this type. (Maybe you meant to use 'yield*'?)");
} }
} }
} }

View File

@ -417,9 +417,12 @@ var Struct = {
} }
}, },
get: function* (op, name) { get: function* (op, name) {
var res = yield* this.getOperation(op.map[name]); var oid = op.map[name];
return (res == null || res.deleted) ? void 0 : (res.opContent == null if (oid != null) {
? res.content : yield* this.getType(res.opContent)); var res = yield* this.getOperation(oid);
return (res == null || res.deleted) ? void 0 : (res.opContent == null
? res.content : yield* this.getType(res.opContent));
}
}, },
set: function* (op, name, value) { set: function* (op, name, value) {
var right = op.map[name] || null; var right = op.map[name] || null;
@ -429,19 +432,13 @@ var Struct = {
parent: op.id, parent: op.id,
parentSub: name parentSub: name
}; };
var oid;
if ( value != null && value._model != null if ( value != null && value._model != null
&& (oid = value._model.id) != null && oid.length === 2) { && value._model.length === 2) {
insert.opContent = oid; insert.opContent = value._model;
} else { } else {
insert.content = value; insert.content = value;
} }
yield* Struct.Insert.create.call(this, insert); yield* Struct.Insert.create.call(this, insert);
if (right != null) {
yield* Struct.Delete.create.call(this, {
target: right
});
}
}, },
delete: function* (op, name) { delete: function* (op, name) {
var v = op.map[name] || null; var v = op.map[name] || null;

View File

@ -8,11 +8,12 @@
} }
*val (pos) { *val (pos) {
var t = yield "transaction"; var t = yield "transaction";
var model = yield* t.getOperation(this._model);
if (pos != null) { if (pos != null) {
var o = yield* Y.Struct.List.ref.call(t, this._model, pos); var o = yield* Y.Struct.List.ref.call(t, model, pos);
return o ? o.content : null; return o ? o.content : null;
} else { } else {
return yield* Y.Struct.List.map.call(t, this._model, function(c){return c; }); return yield* Y.Struct.List.map.call(t, model, function(c){return c; });
} }
} }
*insert (pos, contents) { *insert (pos, contents) {
@ -23,21 +24,25 @@
throw new Error("contents must be an Array of objects!"); throw new Error("contents must be an Array of objects!");
} }
var t = yield "transaction"; var t = yield "transaction";
yield* Y.Struct.List.insert.call(t, this._model, pos, contents); var model = yield* t.getOperation(this._model);
yield* Y.Struct.List.insert.call(t, model, pos, contents);
} }
*delete (pos) { *delete (pos) {
if (typeof pos !== "number") { if (typeof pos !== "number") {
throw new Error("pos must be a number!"); throw new Error("pos must be a number!");
} }
var t = yield "transaction"; var t = yield "transaction";
yield* Y.Struct.List.delete.call(t, this._model, pos); var model = yield* t.getOperation(this._model);
yield* Y.Struct.List.delete.call(t, model, pos);
}
_changed () {
} }
} }
Y.List = function* YList(){ Y.List = function* YList(){
var t = yield "transaction"; var t = yield "transaction";
var model = yield* Y.Struct.List.create.call(t, {type: "List"}); var model = yield* Y.Struct.List.create.call(t, {type: "List"});
return new List(model); return t.createType(model);
}; };
Y.List.Create = List; Y.List.Create = List;
})(); })();

View File

@ -29,13 +29,15 @@
var model = yield* t.getOperation(this._model); var model = yield* t.getOperation(this._model);
yield* Y.Struct.Map.delete.call(t, model, key); yield* Y.Struct.Map.delete.call(t, model, key);
} }
_changed () {
}
} }
Y.Map = function* YMap(){ Y.Map = function* YMap(){
var t = yield "transaction"; var t = yield "transaction";
if (this instanceof Y.AbstractOperationStore) { if (this instanceof Y.AbstractOperationStore) {
var model = yield* Y.Struct.map.create.call(t, {type: "Map"}); var model = yield* Y.Struct.map.create.call(t, {type: "Map"});
return new Map(model); return t.createType(model);
} else { } else {
throw new Error("Don't use `new` to create this type!"); throw new Error("Don't use `new` to create this type!");
} }

View File

@ -8,11 +8,14 @@ class Y { //eslint-disable-line no-unused-vars
this.connector = new Y[opts.connector.name](this, opts.connector); this.connector = new Y[opts.connector.name](this, opts.connector);
this.db.requestTransaction(function*(){ this.db.requestTransaction(function*(){
// create initial Map type // create initial Map type
yield* this.addOperation({ var model = {
id: ["_", 0], id: ["_", 0],
struct: "Map", struct: "Map",
type: "Map",
map: {} map: {}
}); };
yield* this.addOperation(model);
this.createType(model);
}); });
} }
transact (generator) { transact (generator) {

View File

@ -21,7 +21,6 @@ function getRandomNumber(n) {
return Math.floor(Math.random() * n); return Math.floor(Math.random() * n);
} }
var numberOfYMapTests = 30; var numberOfYMapTests = 30;
function applyRandomTransactions (users, transactions, numberOfTransactions) { function applyRandomTransactions (users, transactions, numberOfTransactions) {
@ -94,12 +93,30 @@ describe("Yjs", function(){
}); });
describe("Basic tests", function(){ describe("Basic tests", function(){
it("There is an initial Map type", function(){ it("There is an initial Map type & it is created only once", function(){
var y = this.users[0]; var y = this.users[0];
var root1;
y.transact(function*(root){ y.transact(function*(root){
expect(root).not.toBeUndefined(); expect(root).not.toBeUndefined();
root1 = root;
});
y.transact(function*(root2){
expect(root1).toBe(root2);
}); });
}); });
it("Custom Types are created only once", function(){
var y = this.users[0];
var l1;
y.transact(function*(root){
var l = yield* Y.List();
yield* root.val("list", l);
l1 = l;
});
y.transact(function*(root){
expect(l1).toBe(yield* root.val("list"));
});
});
it("Basic get&set of Map property (converge via sync)", function(){ it("Basic get&set of Map property (converge via sync)", function(){
var y = this.users[0]; var y = this.users[0];
y.transact(function*(root){ y.transact(function*(root){

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