basic get&set of Map properties works
This commit is contained in:
parent
8f63147dbc
commit
bffbb6ca27
@ -53,9 +53,9 @@ var polyfills = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
var files = {
|
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"],
|
lint: ["src/**/*.js", "gulpfile.js"],
|
||||||
test: polyfills.concat(["src/**/*.js"]),
|
test: polyfills.concat(["src/y.js", "src/**/*.js"]),
|
||||||
build_test: ["build_test/y.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, ["build_jasmine_browser"]);
|
||||||
gulp.watch(files.test, ["test"]);
|
gulp.watch(files.test, ["test"]);
|
||||||
|
@ -5,7 +5,8 @@ class AbstractConnector { //eslint-disable-line no-unused-vars
|
|||||||
.role : String Role of this client ("master" or "slave")
|
.role : String Role of this client ("master" or "slave")
|
||||||
.userId : String that uniquely defines the user.
|
.userId : String that uniquely defines the user.
|
||||||
*/
|
*/
|
||||||
constructor (opts) {
|
constructor (y, opts) {
|
||||||
|
this.y = y;
|
||||||
if (opts == null){
|
if (opts == null){
|
||||||
opts = {};
|
opts = {};
|
||||||
}
|
}
|
||||||
@ -23,7 +24,8 @@ class AbstractConnector { //eslint-disable-line no-unused-vars
|
|||||||
this.currentSyncTarget = null;
|
this.currentSyncTarget = null;
|
||||||
}
|
}
|
||||||
setUserId (userId) {
|
setUserId (userId) {
|
||||||
this.os.setUserId(userId);
|
this.userId = userId;
|
||||||
|
this.y.db.setUserId(userId);
|
||||||
}
|
}
|
||||||
onUserEvent (f) {
|
onUserEvent (f) {
|
||||||
this.userEventListeners.push(f);
|
this.userEventListeners.push(f);
|
||||||
|
@ -16,8 +16,9 @@ var globalRoom = {
|
|||||||
users: {},
|
users: {},
|
||||||
buffers: {},
|
buffers: {},
|
||||||
removeUser: function(user){
|
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.users[user];
|
||||||
delete this.buffers[user];
|
delete this.buffers[user];
|
||||||
@ -49,11 +50,11 @@ setInterval(function(){
|
|||||||
var userIdCounter = 0;
|
var userIdCounter = 0;
|
||||||
|
|
||||||
class Test extends AbstractConnector {
|
class Test extends AbstractConnector {
|
||||||
constructor (options) {
|
constructor (y, options) {
|
||||||
if(options === undefined){
|
if(options === undefined){
|
||||||
throw new Error("Options must not be undefined!");
|
throw new Error("Options must not be undefined!");
|
||||||
}
|
}
|
||||||
super({
|
super(y, {
|
||||||
role: "master"
|
role: "master"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -5,10 +5,11 @@ class AbstractTransaction { //eslint-disable-line no-unused-vars
|
|||||||
}
|
}
|
||||||
// returns false if operation is not expected.
|
// returns false if operation is not expected.
|
||||||
*addOperation (op) {
|
*addOperation (op) {
|
||||||
var state = this.getState(op.id[0]);
|
var state = yield* this.getState(op.id[0]);
|
||||||
if (op.id[1] === state.clock){
|
if (op.id[1] === state.clock){
|
||||||
state.clock++;
|
state.clock++;
|
||||||
yield* this.setState(state);
|
yield* this.setState(state);
|
||||||
|
yield* this.setOperation(op);
|
||||||
this.store.operationAdded(op);
|
this.store.operationAdded(op);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@ -16,6 +17,7 @@ class AbstractTransaction { //eslint-disable-line no-unused-vars
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Y.AbstractTransaction = AbstractTransaction;
|
||||||
|
|
||||||
type Listener = {
|
type Listener = {
|
||||||
f : GeneratorFunction, // is called when all operations are available
|
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!
|
a property before you iterate over it!
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
setUserId (userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
apply (ops) {
|
apply (ops) {
|
||||||
for (var o of ops) {
|
for (var o of ops) {
|
||||||
var required = Y.Struct[o.type].requiredOps(o);
|
var required = Y.Struct[o.type].requiredOps(o);
|
||||||
@ -169,3 +174,4 @@ class AbstractOperationStore { //eslint-disable-line no-unused-vars
|
|||||||
ls.push(f);
|
ls.push(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Y.AbstractOperationStore = AbstractOperationStore;
|
||||||
|
@ -113,11 +113,16 @@ Y.IndexedDB = (function(){ //eslint-disable-line no-unused-vars
|
|||||||
if (opts == null) {
|
if (opts == null) {
|
||||||
opts = {};
|
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)!");
|
throw new Error("IndexedDB: expect a string (opts.namespace)!");
|
||||||
} else {
|
} else {
|
||||||
this.namespace = opts.namespace;
|
this.namespace = opts.namespace;
|
||||||
}
|
}
|
||||||
|
if (opts.idbVersion != null) {
|
||||||
|
this.idbVersion = opts.idbVersion;
|
||||||
|
} else {
|
||||||
|
this.idbVersion = 5;
|
||||||
|
}
|
||||||
|
|
||||||
this.transactionQueue = {
|
this.transactionQueue = {
|
||||||
queue: [],
|
queue: [],
|
||||||
@ -127,7 +132,7 @@ Y.IndexedDB = (function(){ //eslint-disable-line no-unused-vars
|
|||||||
var store = this;
|
var store = this;
|
||||||
|
|
||||||
var tGen = (function *transactionGen(){
|
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 transactionQueue = store.transactionQueue;
|
||||||
|
|
||||||
var transaction = null;
|
var transaction = null;
|
||||||
@ -174,8 +179,12 @@ Y.IndexedDB = (function(){ //eslint-disable-line no-unused-vars
|
|||||||
};
|
};
|
||||||
request.onupgradeneeded = function(event){
|
request.onupgradeneeded = function(event){
|
||||||
var db = event.target.result;
|
var db = event.target.result;
|
||||||
db.createObjectStore("OperationStore", {keyPath: "id"});
|
try {
|
||||||
db.createObjectStore("StateVector", {keyPath: "user"});
|
db.createObjectStore("OperationStore", {keyPath: "id"});
|
||||||
|
db.createObjectStore("StateVector", {keyPath: "user"});
|
||||||
|
} catch (e) {
|
||||||
|
// console.log("Store already exists!");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
tGen.throw("You can not yield this type!");
|
tGen.throw("You can not yield this type!");
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
if(typeof window !== "undefined"){
|
if(typeof window !== "undefined"){
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
|
||||||
describe("IndexedDB", function() {
|
describe("IndexedDB", function() {
|
||||||
var ob = new IndexedDB("Test");
|
var ob = new Y.IndexedDB(null, {namespace: "Test"});
|
||||||
|
|
||||||
it("can add and get operation", function(done) {
|
it("can add and get operation", function(done) {
|
||||||
ob.requestTransaction(function*(){
|
ob.requestTransaction(function*(){
|
||||||
|
@ -16,7 +16,7 @@ Y.Memory = (function(){ //eslint-disable-line no-unused-vars
|
|||||||
|
|
||||||
constructor (store : OperationStore) {
|
constructor (store : OperationStore) {
|
||||||
super(store);
|
super(store);
|
||||||
this.sv = store.ss;
|
this.ss = store.ss;
|
||||||
this.os = store.os;
|
this.os = store.os;
|
||||||
}
|
}
|
||||||
*setOperation (op) {
|
*setOperation (op) {
|
||||||
@ -30,7 +30,7 @@ Y.Memory = (function(){ //eslint-disable-line no-unused-vars
|
|||||||
delete this.os[JSON.stringify(id)];
|
delete this.os[JSON.stringify(id)];
|
||||||
}
|
}
|
||||||
*setState (state : State) : State {
|
*setState (state : State) : State {
|
||||||
this.sv[state.user] = state.clock;
|
this.ss[state.user] = state.clock;
|
||||||
}
|
}
|
||||||
*getState (user : string) : State {
|
*getState (user : string) : State {
|
||||||
var clock = this.ss[user];
|
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
|
class OperationStore extends AbstractOperationStore { //eslint-disable-line no-undef
|
||||||
namespace: string;
|
|
||||||
ready: Promise;
|
|
||||||
whenReadyListeners: Array<Function>;
|
|
||||||
constructor (y) {
|
constructor (y) {
|
||||||
super(y);
|
super(y);
|
||||||
|
this.os = {};
|
||||||
|
this.ss = {};
|
||||||
}
|
}
|
||||||
requestTransaction (makeGen : Function) {
|
requestTransaction (makeGen : Function) {
|
||||||
var t = new Transaction(this);
|
var t = new Transaction(this);
|
||||||
var gen = makeGen.call(t);
|
var gen = makeGen.call(t);
|
||||||
gen.next();
|
var res = gen.next();
|
||||||
if (gen.done !== true) {
|
while(!res.done){
|
||||||
throw new Error("transaction is supposed to be done. Note: you may not yield with this transaction! (yield* is allowed though)");
|
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 () {
|
*removeDatabase () {
|
||||||
|
@ -19,6 +19,21 @@ type Insert = {
|
|||||||
content: any
|
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 = {
|
var Struct = {
|
||||||
Operation: { //eslint-disable-line no-unused-vars
|
Operation: { //eslint-disable-line no-unused-vars
|
||||||
create: function*(op : Op) : Struct.Operation {
|
create: function*(op : Op) : Struct.Operation {
|
||||||
@ -55,6 +70,25 @@ var Struct = {
|
|||||||
op.right.left = op.id;
|
op.right.left = op.id;
|
||||||
yield* this.setOperation(op.right);
|
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;
|
return op;
|
||||||
},
|
},
|
||||||
requiredOps: function(op, ids){
|
requiredOps: function(op, ids){
|
||||||
@ -209,6 +243,11 @@ var Struct = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Map: {
|
Map: {
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
*/
|
||||||
create: function*( op : Op ){
|
create: function*( op : Op ){
|
||||||
op.map = {};
|
op.map = {};
|
||||||
op.struct = "Map";
|
op.struct = "Map";
|
||||||
@ -224,7 +263,7 @@ var Struct = {
|
|||||||
// nop
|
// nop
|
||||||
},
|
},
|
||||||
get: function* (op, name) {
|
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) {
|
set: function* (op, name, value) {
|
||||||
var end = op.map[name];
|
var end = op.map[name];
|
||||||
@ -243,3 +282,5 @@ var Struct = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Y.Struct = Struct;
|
||||||
|
29
src/Types/Map.js
Normal file
29
src/Types/Map.js
Normal 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;
|
||||||
|
})();
|
@ -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;
|
|
||||||
})();
|
|
16
src/y.js
16
src/y.js
@ -1,11 +1,25 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
|
const GeneratorFunction = (function*(){}).constructor;
|
||||||
|
|
||||||
class Y { //eslint-disable-line no-unused-vars
|
class Y { //eslint-disable-line no-unused-vars
|
||||||
constructor (opts) {
|
constructor (opts) {
|
||||||
this.connector = new Y[opts.connector.name](opts.connector);
|
|
||||||
this.db = new Y[opts.db.name](this, opts.db);
|
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) {
|
transact (generator) {
|
||||||
|
if (generator.constructor !== GeneratorFunction) {
|
||||||
|
throw new Error("y.transact requires a Generator function! E.g. function*(){/*..*/}");
|
||||||
|
}
|
||||||
this.db.requestTransaction(generator);
|
this.db.requestTransaction(generator);
|
||||||
}
|
}
|
||||||
destroy () {
|
destroy () {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
/*eslint-env browser,jasmine */
|
/*eslint-env browser,jasmine */
|
||||||
|
|
||||||
describe("Yjs (basic)", function(){
|
describe("Yjs (basic)", function(){
|
||||||
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = 500;
|
||||||
beforeEach(function(){
|
beforeEach(function(){
|
||||||
this.users = [];
|
this.users = [];
|
||||||
for (var i = 0; i < 5; i++) {
|
for (var i = 0; i < 5; i++) {
|
||||||
@ -21,9 +22,18 @@ describe("Yjs (basic)", function(){
|
|||||||
}
|
}
|
||||||
this.users = [];
|
this.users = [];
|
||||||
});
|
});
|
||||||
it("can List.insert and get value from the other user", function(done){
|
it("There is an initial Map type", function(done){
|
||||||
this.users[0].val("name", 1);
|
var y = this.users[0];
|
||||||
this.users[0].connector.whenSynced(function(){
|
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();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user