implemented module loader for yjs
This commit is contained in:
parent
138afe39dc
commit
6dc347642b
@ -21,31 +21,10 @@ module.exports = function (gulp, helperOptions) {
|
|||||||
options.regenerator = false
|
options.regenerator = false
|
||||||
// TODO: include './node_modules/gulp-babel/node_modules/babel-core/node_modules/regenerator/runtime.js'
|
// TODO: include './node_modules/gulp-babel/node_modules/babel-core/node_modules/regenerator/runtime.js'
|
||||||
}
|
}
|
||||||
var concatOrder = [
|
|
||||||
'y.js',
|
|
||||||
'Connector.js',
|
|
||||||
'Database.js',
|
|
||||||
'Transaction.js',
|
|
||||||
'Struct.js',
|
|
||||||
'Utils.js',
|
|
||||||
'Databases/RedBlackTree.js',
|
|
||||||
'Databases/Memory.js',
|
|
||||||
'Databases/IndexedDB.js',
|
|
||||||
'Connectors/Test.js',
|
|
||||||
'Types/Array.js',
|
|
||||||
'Types/Map.js',
|
|
||||||
'Types/TextBind.js'
|
|
||||||
]
|
|
||||||
var yjsfiles = concatOrder.map(function (f) {
|
|
||||||
return '../yjs/src/' + f
|
|
||||||
})
|
|
||||||
var files = {
|
var files = {
|
||||||
dist: helperOptions.polyfills.concat(helperOptions.files.map(function (f) {
|
dist: helperOptions.entry,
|
||||||
return 'src/' + f
|
specs: helperOptions.specs,
|
||||||
})),
|
src: './src/**/*.js'
|
||||||
test: ['../yjs/src/Helper.spec.js'].concat(yjsfiles).concat(helperOptions.files.map(function (f) {
|
|
||||||
return 'src/' + f
|
|
||||||
}).concat(['src/' + options.testfiles]))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var babelOptions = {
|
var babelOptions = {
|
||||||
@ -54,38 +33,10 @@ module.exports = function (gulp, helperOptions) {
|
|||||||
experimental: true
|
experimental: true
|
||||||
}
|
}
|
||||||
if (options.regenerator) {
|
if (options.regenerator) {
|
||||||
files.test = helperOptions.polyfills.concat(files.test)
|
files.specs = helperOptions.polyfills.concat(files.specs)
|
||||||
} else {
|
} else {
|
||||||
babelOptions.blacklist = 'regenerator'
|
babelOptions.blacklist = 'regenerator'
|
||||||
}
|
}
|
||||||
// babelOptions.blacklist = 'regenerator'
|
|
||||||
|
|
||||||
gulp.task('dist', ['build:dist'], function () {
|
|
||||||
function createDist (pipe) {
|
|
||||||
return pipe
|
|
||||||
.pipe($.if(options.debug, $.sourcemaps.init({loadMaps: true})))
|
|
||||||
.pipe($.concat(options.targetName))
|
|
||||||
.pipe($.if(!options.debug && options.regenerator, $.uglify()))
|
|
||||||
.pipe($.if(options.debug, $.sourcemaps.write('.')))
|
|
||||||
.pipe(gulp.dest('./dist/'))
|
|
||||||
}
|
|
||||||
var pipe
|
|
||||||
if (options.browserify || true) {
|
|
||||||
var browserify = require('browserify')
|
|
||||||
var source = require('vinyl-source-stream')
|
|
||||||
var buffer = require('vinyl-buffer')
|
|
||||||
|
|
||||||
pipe = browserify({
|
|
||||||
entries: 'build/' + options.targetName,
|
|
||||||
debug: options.debug
|
|
||||||
}).bundle()
|
|
||||||
.pipe(source(options.targetName))
|
|
||||||
.pipe(buffer())
|
|
||||||
} else {
|
|
||||||
pipe = gulp.src('build/' + options.targetName)
|
|
||||||
}
|
|
||||||
return createDist(pipe)
|
|
||||||
})
|
|
||||||
|
|
||||||
gulp.task('dist', function () {
|
gulp.task('dist', function () {
|
||||||
var browserify = require('browserify')
|
var browserify = require('browserify')
|
||||||
@ -99,7 +50,7 @@ module.exports = function (gulp, helperOptions) {
|
|||||||
.pipe(source(options.targetName))
|
.pipe(source(options.targetName))
|
||||||
.pipe(buffer())
|
.pipe(buffer())
|
||||||
.pipe($.if(options.debug, $.sourcemaps.init({loadMaps: true})))
|
.pipe($.if(options.debug, $.sourcemaps.init({loadMaps: true})))
|
||||||
.pipe($.concat(options.targetName))
|
.pipe($.if(!options.debug && options.regenerator, $.babel(babelOptions)))
|
||||||
.pipe($.if(!options.debug && options.regenerator, $.uglify()))
|
.pipe($.if(!options.debug && options.regenerator, $.uglify()))
|
||||||
.pipe($.if(options.debug, $.sourcemaps.write('.')))
|
.pipe($.if(options.debug, $.sourcemaps.write('.')))
|
||||||
.pipe(gulp.dest('./dist/'))
|
.pipe(gulp.dest('./dist/'))
|
||||||
@ -108,11 +59,46 @@ module.exports = function (gulp, helperOptions) {
|
|||||||
gulp.task('watch:dist', function (cb) {
|
gulp.task('watch:dist', function (cb) {
|
||||||
options.debug = true
|
options.debug = true
|
||||||
runSequence('dist', function () {
|
runSequence('dist', function () {
|
||||||
gulp.watch(files.dist, ['dist'])
|
gulp.watch(files.src, ['dist'])
|
||||||
cb()
|
cb()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
gulp.task('dev:node', ['test'], function () {
|
||||||
|
gulp.watch(files.src, ['test'])
|
||||||
|
})
|
||||||
|
|
||||||
|
gulp.task('spec-build', function () {
|
||||||
|
var browserify = require('browserify')
|
||||||
|
var source = require('vinyl-source-stream')
|
||||||
|
var buffer = require('vinyl-buffer')
|
||||||
|
|
||||||
|
return browserify({
|
||||||
|
entries: files.specs,
|
||||||
|
debug: options.debug
|
||||||
|
}).bundle()
|
||||||
|
.pipe(source('specs.js'))
|
||||||
|
.pipe(buffer())
|
||||||
|
.pipe($.sourcemaps.init({loadMaps: true}))
|
||||||
|
.pipe($.sourcemaps.write())
|
||||||
|
.pipe(gulp.dest('./build/'))
|
||||||
|
})
|
||||||
|
|
||||||
|
gulp.task('dev:browser', ['spec-build'], function () {
|
||||||
|
gulp.watch(files.src, ['spec-build'])
|
||||||
|
return gulp.src('./build/specs.js')
|
||||||
|
.pipe($.jasmineBrowser.specRunner())
|
||||||
|
.pipe($.jasmineBrowser.server({port: options.testport}))
|
||||||
|
})
|
||||||
|
|
||||||
|
gulp.task('test', function () {
|
||||||
|
return gulp.src(files.specs)
|
||||||
|
.pipe($.jasmine({
|
||||||
|
verbose: true,
|
||||||
|
includeStuckTrace: true
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
gulp.task('updateSubmodule', function () {
|
gulp.task('updateSubmodule', function () {
|
||||||
return gulp.src('./package.json', {read: false})
|
return gulp.src('./package.json', {read: false})
|
||||||
.pipe($.shell([
|
.pipe($.shell([
|
||||||
@ -168,24 +154,4 @@ module.exports = function (gulp, helperOptions) {
|
|||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
gulp.task('dev:node', ['test'], function () {
|
|
||||||
gulp.watch(files.dist, ['test'])
|
|
||||||
})
|
|
||||||
|
|
||||||
gulp.task('dev:browser', ['watch:build'], function () {
|
|
||||||
return gulp.src(files.test)
|
|
||||||
.pipe($.watch(['build/**/*']))
|
|
||||||
.pipe($.jasmineBrowser.specRunner())
|
|
||||||
.pipe($.jasmineBrowser.server({port: options.testport}))
|
|
||||||
})
|
|
||||||
|
|
||||||
gulp.task('test', function () {
|
|
||||||
console.log(files.test)
|
|
||||||
return gulp.src('./dist/y.js')
|
|
||||||
.pipe($.jasmine({
|
|
||||||
verbose: true,
|
|
||||||
includeStuckTrace: true
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
24
gulpfile.js
24
gulpfile.js
@ -49,23 +49,15 @@ var runSequence = require('run-sequence').use(gulp)
|
|||||||
|
|
||||||
require('./gulpfile.helper.js')(gulp, {
|
require('./gulpfile.helper.js')(gulp, {
|
||||||
polyfills: [],
|
polyfills: [],
|
||||||
files: [
|
entry: './src/y.js',
|
||||||
'y.js',
|
|
||||||
'Connector.js',
|
|
||||||
'Database.js',
|
|
||||||
'Transaction.js',
|
|
||||||
'Struct.js',
|
|
||||||
'Utils.js',
|
|
||||||
'Databases/RedBlackTree.js',
|
|
||||||
'Databases/Memory.js',
|
|
||||||
'Databases/IndexedDB.js',
|
|
||||||
'Connectors/Test.js',
|
|
||||||
'Types/Array.js',
|
|
||||||
'Types/Map.js',
|
|
||||||
'Types/TextBind.js'
|
|
||||||
],
|
|
||||||
targetName: 'y.js',
|
targetName: 'y.js',
|
||||||
moduleName: 'yjs'
|
moduleName: 'yjs',
|
||||||
|
specs: [
|
||||||
|
'./src/Databases/RedBlackTree.spec.js',
|
||||||
|
'./src/Types/Array.spec.js',
|
||||||
|
'./src/Types/Map.spec.js',
|
||||||
|
'./src/Database.spec.js'
|
||||||
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
gulp.task('dev:examples', ['updateSubmodule', 'watch:dist'], function () {
|
gulp.task('dev:examples', ['updateSubmodule', 'watch:dist'], function () {
|
||||||
|
@ -68,6 +68,7 @@
|
|||||||
"run-sequence": "^1.1.4",
|
"run-sequence": "^1.1.4",
|
||||||
"standard": "^5.2.2",
|
"standard": "^5.2.2",
|
||||||
"vinyl-buffer": "^1.0.0",
|
"vinyl-buffer": "^1.0.0",
|
||||||
"vinyl-source-stream": "^1.1.0"
|
"vinyl-source-stream": "^1.1.0",
|
||||||
|
"watchify": "^3.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/* globals Y */
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
class AbstractConnector {
|
module.exports = function (Y) {
|
||||||
|
class AbstractConnector {
|
||||||
/*
|
/*
|
||||||
opts contains the following information:
|
opts contains the following information:
|
||||||
role : String Role of this client ("master" or "slave")
|
role : String Role of this client ("master" or "slave")
|
||||||
@ -324,5 +324,6 @@ class AbstractConnector {
|
|||||||
throw new Error("I can't encode this json!")
|
throw new Error("I can't encode this json!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Y.AbstractConnector = AbstractConnector
|
||||||
}
|
}
|
||||||
Y.AbstractConnector = AbstractConnector
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
/* global getRandom, Y, wait, async */
|
/* global getRandom, wait, async */
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
var globalRoom = {
|
module.exports = function (Y) {
|
||||||
|
var globalRoom = {
|
||||||
users: {},
|
users: {},
|
||||||
buffers: {},
|
buffers: {},
|
||||||
removeUser: function (user) {
|
removeUser: function (user) {
|
||||||
@ -22,10 +23,10 @@ var globalRoom = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Y.utils.globalRoom = globalRoom
|
Y.utils.globalRoom = globalRoom
|
||||||
|
|
||||||
function flushOne () {
|
function flushOne () {
|
||||||
var bufs = []
|
var bufs = []
|
||||||
for (var i in globalRoom.buffers) {
|
for (var i in globalRoom.buffers) {
|
||||||
if (globalRoom.buffers[i].length > 0) {
|
if (globalRoom.buffers[i].length > 0) {
|
||||||
@ -41,13 +42,13 @@ function flushOne () {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// setInterval(flushOne, 10)
|
// setInterval(flushOne, 10)
|
||||||
|
|
||||||
var userIdCounter = 0
|
var userIdCounter = 0
|
||||||
|
|
||||||
class Test extends Y.AbstractConnector {
|
class Test extends Y.AbstractConnector {
|
||||||
constructor (y, 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!')
|
||||||
@ -131,6 +132,7 @@ class Test extends Y.AbstractConnector {
|
|||||||
flushOne () {
|
flushOne () {
|
||||||
flushOne()
|
flushOne()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Y.Test = Test
|
Y.Test = Test
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/* global Y */
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
/*
|
module.exports = function (Y) {
|
||||||
|
/*
|
||||||
Partial definition of an OperationStore.
|
Partial definition of an OperationStore.
|
||||||
TODO: name it Database, operation store only holds operations.
|
TODO: name it Database, operation store only holds operations.
|
||||||
|
|
||||||
@ -12,8 +12,8 @@
|
|||||||
- request a transaction
|
- request a transaction
|
||||||
* destroy()
|
* destroy()
|
||||||
- destroy the database
|
- destroy the database
|
||||||
*/
|
*/
|
||||||
class AbstractDatabase {
|
class AbstractDatabase {
|
||||||
constructor (y, opts) {
|
constructor (y, opts) {
|
||||||
this.y = y
|
this.y = y
|
||||||
// E.g. this.listenersById[id] : Array<Listener>
|
// E.g. this.listenersById[id] : Array<Listener>
|
||||||
@ -337,5 +337,6 @@ class AbstractDatabase {
|
|||||||
this.waitingTransactions.push(makeGen)
|
this.waitingTransactions.push(makeGen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Y.AbstractDatabase = AbstractDatabase
|
||||||
}
|
}
|
||||||
Y.AbstractDatabase = AbstractDatabase
|
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
/* global Y, async, databases */
|
/* global async, databases */
|
||||||
/* eslint-env browser,jasmine,console */
|
/* eslint-env browser,jasmine,console */
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
var Y = require('./SpecHelper.js')
|
||||||
|
|
||||||
for (let database of databases) {
|
for (let database of databases) {
|
||||||
describe(`Database (${database})`, function () {
|
describe(`Database (${database})`, function () {
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
/* global Y */
|
|
||||||
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
Y.IndexedDB = (function () {
|
module.exports = function (Y) {
|
||||||
class Store {
|
class Store {
|
||||||
constructor (transaction, name) {
|
constructor (transaction, name) {
|
||||||
this.store = transaction.objectStore(name)
|
this.store = transaction.objectStore(name)
|
||||||
@ -177,5 +175,5 @@ Y.IndexedDB = (function () {
|
|||||||
yield window.indexedDB.deleteDatabase(this.namespace)
|
yield window.indexedDB.deleteDatabase(this.namespace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return OperationStore
|
Y.IndexedDB = OperationStore
|
||||||
})()
|
}
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
/* global Y */
|
|
||||||
/* eslint-env browser,jasmine */
|
|
||||||
|
|
||||||
if (typeof window !== 'undefined' && false) {
|
|
||||||
describe('IndexedDB', function () {
|
|
||||||
var ob
|
|
||||||
beforeAll(function () {
|
|
||||||
ob = new Y.IndexedDB(null, {namespace: 'Test', gcTimeout: -1})
|
|
||||||
})
|
|
||||||
|
|
||||||
afterAll(function (done) {
|
|
||||||
ob.requestTransaction(function *() {
|
|
||||||
yield* ob.removeDatabase()
|
|
||||||
ob = null
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,7 +1,6 @@
|
|||||||
/* global Y */
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
Y.Memory = (function () {
|
module.exports = function (Y) {
|
||||||
class Transaction extends Y.Transaction {
|
class Transaction extends Y.Transaction {
|
||||||
constructor (store) {
|
constructor (store) {
|
||||||
super(store)
|
super(store)
|
||||||
@ -59,5 +58,5 @@ Y.Memory = (function () {
|
|||||||
delete this.ds
|
delete this.ds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Database
|
Y.Memory = Database
|
||||||
})()
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
/* global Y */
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This file contains a not so fancy implemantion of a Red Black Tree.
|
This file contains a not so fancy implemantion of a Red Black Tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class N {
|
module.exports = function (Y) {
|
||||||
|
class N {
|
||||||
// A created node is always red!
|
// A created node is always red!
|
||||||
constructor (val) {
|
constructor (val) {
|
||||||
this.val = val
|
this.val = val
|
||||||
@ -123,9 +123,9 @@ class N {
|
|||||||
return this.parent.parent.left
|
return this.parent.parent.left
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RBTree {
|
class RBTree {
|
||||||
constructor () {
|
constructor () {
|
||||||
this.root = null
|
this.root = null
|
||||||
this.length = 0
|
this.length = 0
|
||||||
@ -484,6 +484,7 @@ class RBTree {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Y.utils.RBTree = RBTree
|
Y.utils.RBTree = RBTree
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/* global Y */
|
|
||||||
/* eslint-env browser,jasmine,console */
|
/* eslint-env browser,jasmine,console */
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
var Y = require('../SpecHelper.js')
|
||||||
var numberOfRBTreeTests = 1000
|
var numberOfRBTreeTests = 1000
|
||||||
|
|
||||||
function itRedNodesDoNotHaveBlackChildren () {
|
function itRedNodesDoNotHaveBlackChildren () {
|
||||||
|
@ -1,288 +0,0 @@
|
|||||||
/* global Y */
|
|
||||||
/* eslint-env browser, jasmine */
|
|
||||||
|
|
||||||
/*
|
|
||||||
This is just a compilation of functions that help to test this library!
|
|
||||||
*/
|
|
||||||
|
|
||||||
// When testing, you store everything on the global object. We call it g
|
|
||||||
var g
|
|
||||||
if (typeof global !== 'undefined') {
|
|
||||||
g = global
|
|
||||||
} else if (typeof window !== 'undefined') {
|
|
||||||
g = window
|
|
||||||
} else {
|
|
||||||
throw new Error('No global object?')
|
|
||||||
}
|
|
||||||
g.g = g
|
|
||||||
|
|
||||||
g.YConcurrency_TestingMode = true
|
|
||||||
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000
|
|
||||||
|
|
||||||
g.describeManyTimes = function describeManyTimes (times, name, f) {
|
|
||||||
for (var i = 0; i < times; i++) {
|
|
||||||
describe(name, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Wait for a specified amount of time (in ms). defaults to 5ms
|
|
||||||
*/
|
|
||||||
function wait (t) {
|
|
||||||
if (t == null) {
|
|
||||||
t = 5
|
|
||||||
}
|
|
||||||
return new Promise(function (resolve) {
|
|
||||||
setTimeout(function () {
|
|
||||||
resolve()
|
|
||||||
}, t * 2)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
g.wait = wait
|
|
||||||
|
|
||||||
g.databases = ['Memory']
|
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
g.databases.push('IndexedDB')
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
returns a random element of o.
|
|
||||||
works on Object, and Array
|
|
||||||
*/
|
|
||||||
function getRandom (o) {
|
|
||||||
if (o instanceof Array) {
|
|
||||||
return o[Math.floor(Math.random() * o.length)]
|
|
||||||
} else if (o.constructor === Object) {
|
|
||||||
var ks = []
|
|
||||||
for (var key in o) {
|
|
||||||
ks.push(key)
|
|
||||||
}
|
|
||||||
return o[getRandom(ks)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.getRandom = getRandom
|
|
||||||
|
|
||||||
function getRandomNumber (n) {
|
|
||||||
if (n == null) {
|
|
||||||
n = 9999
|
|
||||||
}
|
|
||||||
return Math.floor(Math.random() * n)
|
|
||||||
}
|
|
||||||
g.getRandomNumber = getRandomNumber
|
|
||||||
|
|
||||||
function * applyTransactions (relAmount, numberOfTransactions, objects, users, transactions) {
|
|
||||||
function randomTransaction (root) {
|
|
||||||
var f = getRandom(transactions)
|
|
||||||
f(root)
|
|
||||||
}
|
|
||||||
for (var i = 0; i < numberOfTransactions * relAmount + 1; i++) {
|
|
||||||
var r = Math.random()
|
|
||||||
if (r >= 0.5) {
|
|
||||||
// 50% chance to flush
|
|
||||||
users[0].connector.flushOne() // flushes for some user.. (not necessarily 0)
|
|
||||||
} else if (r >= 0.05) {
|
|
||||||
// 45% chance to create operation
|
|
||||||
randomTransaction(getRandom(objects))
|
|
||||||
} else {
|
|
||||||
// 5% chance to disconnect/reconnect
|
|
||||||
var u = getRandom(users)
|
|
||||||
if (u.connector.isDisconnected()) {
|
|
||||||
yield u.reconnect()
|
|
||||||
} else {
|
|
||||||
yield u.disconnect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
yield wait()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g.applyRandomTransactionsAllRejoinNoGC = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) {
|
|
||||||
yield* applyTransactions(1, numberOfTransactions, objects, users, transactions)
|
|
||||||
yield users[0].connector.flushAll()
|
|
||||||
yield wait()
|
|
||||||
for (var u in users) {
|
|
||||||
yield users[u].reconnect()
|
|
||||||
}
|
|
||||||
yield wait(100)
|
|
||||||
yield users[0].connector.flushAll()
|
|
||||||
yield g.garbageCollectAllUsers(users)
|
|
||||||
})
|
|
||||||
|
|
||||||
g.applyRandomTransactionsWithGC = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) {
|
|
||||||
yield* applyTransactions(1, numberOfTransactions, objects, users.slice(1), transactions)
|
|
||||||
yield users[0].connector.flushAll()
|
|
||||||
yield g.garbageCollectAllUsers(users)
|
|
||||||
yield wait(100)
|
|
||||||
for (var u in users) {
|
|
||||||
// TODO: here, we enforce that two users never sync at the same time with u[0]
|
|
||||||
// enforce that in the connector itself!
|
|
||||||
yield users[u].reconnect()
|
|
||||||
}
|
|
||||||
yield wait(100)
|
|
||||||
yield users[0].connector.flushAll()
|
|
||||||
yield wait(100)
|
|
||||||
yield g.garbageCollectAllUsers(users)
|
|
||||||
})
|
|
||||||
|
|
||||||
g.garbageCollectAllUsers = async(function * garbageCollectAllUsers (users) {
|
|
||||||
// gc two times because of the two gc phases (really collect everything)
|
|
||||||
yield wait(100)
|
|
||||||
for (var i in users) {
|
|
||||||
yield users[i].db.garbageCollect()
|
|
||||||
yield users[i].db.garbageCollect()
|
|
||||||
}
|
|
||||||
yield wait(100)
|
|
||||||
})
|
|
||||||
|
|
||||||
g.compareAllUsers = async(function * compareAllUsers (users) {
|
|
||||||
var s1, s2 // state sets
|
|
||||||
var ds1, ds2 // delete sets
|
|
||||||
var allDels1, allDels2 // all deletions
|
|
||||||
var db1 = [] // operation store of user1
|
|
||||||
|
|
||||||
// t1 and t2 basically do the same. They define t[1,2], ds[1,2], and allDels[1,2]
|
|
||||||
function * t1 () {
|
|
||||||
s1 = yield* this.getStateSet()
|
|
||||||
ds1 = yield* this.getDeleteSet()
|
|
||||||
allDels1 = []
|
|
||||||
yield* this.ds.iterate(this, null, null, function * (d) {
|
|
||||||
allDels1.push(d)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
function * t2 () {
|
|
||||||
s2 = yield* this.getStateSet()
|
|
||||||
ds2 = yield* this.getDeleteSet()
|
|
||||||
allDels2 = []
|
|
||||||
yield* this.ds.iterate(this, null, null, function * (d) {
|
|
||||||
allDels2.push(d)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
yield users[0].connector.flushAll()
|
|
||||||
yield wait()
|
|
||||||
yield g.garbageCollectAllUsers(users)
|
|
||||||
|
|
||||||
for (var uid = 0; uid < users.length; uid++) {
|
|
||||||
var u = users[uid]
|
|
||||||
u.db.requestTransaction(function * () {
|
|
||||||
// compare deleted ops against deleteStore
|
|
||||||
yield* this.os.iterate(this, null, null, function * (o) {
|
|
||||||
if (o.deleted === true) {
|
|
||||||
expect(yield* this.isDeleted(o.id)).toBeTruthy()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// compare deleteStore against deleted ops
|
|
||||||
var ds = []
|
|
||||||
yield* this.ds.iterate(this, null, null, function * (d) {
|
|
||||||
ds.push(d)
|
|
||||||
})
|
|
||||||
for (var j in ds) {
|
|
||||||
var d = ds[j]
|
|
||||||
for (var i = 0; i < d.len; i++) {
|
|
||||||
var o = yield* this.getOperation([d.id[0], d.id[1] + i])
|
|
||||||
// gc'd or deleted
|
|
||||||
if (d.gc) {
|
|
||||||
expect(o).toBeFalsy()
|
|
||||||
} else {
|
|
||||||
expect(o.deleted).toBeTruthy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// compare allDels tree
|
|
||||||
yield wait()
|
|
||||||
if (s1 == null) {
|
|
||||||
u.db.requestTransaction(function * () {
|
|
||||||
yield* t1.call(this)
|
|
||||||
yield* this.os.iterate(this, null, null, function * (o) {
|
|
||||||
o = Y.utils.copyObject(o)
|
|
||||||
delete o.origin
|
|
||||||
db1.push(o)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
yield wait()
|
|
||||||
} else {
|
|
||||||
// TODO: make requestTransaction return a promise..
|
|
||||||
u.db.requestTransaction(function * () {
|
|
||||||
yield* t2.call(this)
|
|
||||||
expect(s1).toEqual(s2)
|
|
||||||
expect(allDels1).toEqual(allDels2) // inner structure
|
|
||||||
expect(ds1).toEqual(ds2) // exported structure
|
|
||||||
var count = 0
|
|
||||||
yield* this.os.iterate(this, null, null, function * (o) {
|
|
||||||
o = Y.utils.copyObject(o)
|
|
||||||
delete o.origin
|
|
||||||
expect(db1[count++]).toEqual(o)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
yield wait()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
g.createUsers = async(function * createUsers (self, numberOfUsers, database) {
|
|
||||||
if (Y.utils.globalRoom.users[0] != null) {
|
|
||||||
yield Y.utils.globalRoom.users[0].flushAll()
|
|
||||||
}
|
|
||||||
// destroy old users
|
|
||||||
for (var u in Y.utils.globalRoom.users) {
|
|
||||||
Y.utils.globalRoom.users[u].y.destroy()
|
|
||||||
}
|
|
||||||
self.users = null
|
|
||||||
|
|
||||||
var promises = []
|
|
||||||
for (var i = 0; i < numberOfUsers; i++) {
|
|
||||||
promises.push(Y({
|
|
||||||
db: {
|
|
||||||
name: database,
|
|
||||||
namespace: 'User ' + i,
|
|
||||||
cleanStart: true,
|
|
||||||
gcTimeout: -1
|
|
||||||
},
|
|
||||||
connector: {
|
|
||||||
name: 'Test',
|
|
||||||
debug: false
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
self.users = yield Promise.all(promises)
|
|
||||||
return self.users
|
|
||||||
})
|
|
||||||
|
|
||||||
/*
|
|
||||||
Until async/await arrives in js, we use this function to wait for promises
|
|
||||||
by yielding them.
|
|
||||||
*/
|
|
||||||
function async (makeGenerator) {
|
|
||||||
return function (arg) {
|
|
||||||
var generator = makeGenerator.apply(this, arguments)
|
|
||||||
|
|
||||||
function handle (result) {
|
|
||||||
if (result.done) return Promise.resolve(result.value)
|
|
||||||
|
|
||||||
return Promise.resolve(result.value).then(function (res) {
|
|
||||||
return handle(generator.next(res))
|
|
||||||
}, function (err) {
|
|
||||||
return handle(generator.throw(err))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return handle(generator.next())
|
|
||||||
} catch (ex) {
|
|
||||||
generator.throw(ex)
|
|
||||||
// return Promise.reject(ex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.async = async
|
|
||||||
|
|
||||||
function logUsers (self) {
|
|
||||||
if (self.constructor === Array) {
|
|
||||||
self = {users: self}
|
|
||||||
}
|
|
||||||
self.users[0].db.logTable()
|
|
||||||
self.users[1].db.logTable()
|
|
||||||
self.users[2].db.logTable()
|
|
||||||
}
|
|
||||||
|
|
||||||
g.logUsers = logUsers
|
|
@ -1,4 +1,3 @@
|
|||||||
/* global Y */
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -19,8 +18,8 @@
|
|||||||
* requiredOps
|
* requiredOps
|
||||||
- Operations that are required to execute this operation.
|
- Operations that are required to execute this operation.
|
||||||
*/
|
*/
|
||||||
|
module.exports = function (Y) {
|
||||||
var Struct = {
|
var Struct = {
|
||||||
/* This is the only operation that is actually not a structure, because
|
/* This is the only operation that is actually not a structure, because
|
||||||
it is not stored in the OS. This is why it _does not_ have an id
|
it is not stored in the OS. This is why it _does not_ have an id
|
||||||
|
|
||||||
@ -332,5 +331,6 @@ var Struct = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Y.Struct = Struct
|
||||||
}
|
}
|
||||||
Y.Struct = Struct
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global Y */
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -74,7 +73,8 @@
|
|||||||
- this is called only by `getOperations(startSS)`. It makes an operation
|
- this is called only by `getOperations(startSS)`. It makes an operation
|
||||||
applyable on a given SS.
|
applyable on a given SS.
|
||||||
*/
|
*/
|
||||||
class Transaction {
|
module.exports = function (Y) {
|
||||||
|
class Transaction {
|
||||||
/*
|
/*
|
||||||
Get a type based on the id of its model.
|
Get a type based on the id of its model.
|
||||||
If it does not exist yes, create it.
|
If it does not exist yes, create it.
|
||||||
@ -649,5 +649,6 @@ class Transaction {
|
|||||||
op.left = op.origin
|
op.left = op.origin
|
||||||
return op
|
return op
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Y.Transaction = Transaction
|
||||||
}
|
}
|
||||||
Y.Transaction = Transaction
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
/* global Y */
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
;(function () {
|
function extend (Y) {
|
||||||
class YArray {
|
class YArray {
|
||||||
constructor (os, _model, idArray, valArray) {
|
constructor (os, _model, idArray, valArray) {
|
||||||
this.os = os
|
this.os = os
|
||||||
@ -166,7 +165,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Y.Array = new Y.utils.CustomType({
|
Y.extend('Array', new Y.utils.CustomType({
|
||||||
class: YArray,
|
class: YArray,
|
||||||
createType: function * YArrayCreator () {
|
createType: function * YArrayCreator () {
|
||||||
var modelid = this.store.getNextOpId()
|
var modelid = this.store.getNextOpId()
|
||||||
@ -188,5 +187,11 @@
|
|||||||
})
|
})
|
||||||
return new YArray(os, model.id, idArray, valArray)
|
return new YArray(os, model.id, idArray, valArray)
|
||||||
}
|
}
|
||||||
})
|
}))
|
||||||
})()
|
}
|
||||||
|
|
||||||
|
if (typeof Y !== 'undefined') {
|
||||||
|
extend(Y)
|
||||||
|
} else {
|
||||||
|
module.exports = extend
|
||||||
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
/* global createUsers, databases, wait, Y, compareAllUsers, getRandomNumber, applyRandomTransactionsAllRejoinNoGC, applyRandomTransactionsWithGC, async, garbageCollectAllUsers, describeManyTimes */
|
/* global createUsers, databases, wait, compareAllUsers, getRandomNumber, applyRandomTransactionsAllRejoinNoGC, applyRandomTransactionsWithGC, async, garbageCollectAllUsers, describeManyTimes */
|
||||||
/* eslint-env browser,jasmine */
|
/* eslint-env browser,jasmine */
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
var Y = require('../SpecHelper.js')
|
||||||
var numberOfYArrayTests = 10
|
var numberOfYArrayTests = 10
|
||||||
var repeatArrayTests = 2
|
var repeatArrayTests = 2
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
/* global Y */
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
;(function () {
|
module.exports = function (Y) {
|
||||||
class YMap {
|
class YMap {
|
||||||
constructor (os, model, contents, opContents) {
|
constructor (os, model, contents, opContents) {
|
||||||
this._model = model.id
|
this._model = model.id
|
||||||
@ -292,4 +291,4 @@
|
|||||||
return new YMap(os, model, contents, opContents)
|
return new YMap(os, model, contents, opContents)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})()
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
/* global createUsers, Y, databases, compareAllUsers, getRandomNumber, applyRandomTransactionsAllRejoinNoGC, applyRandomTransactionsWithGC, async, describeManyTimes */
|
/* global createUsers, databases, compareAllUsers, getRandomNumber, applyRandomTransactionsAllRejoinNoGC, applyRandomTransactionsWithGC, async, describeManyTimes */
|
||||||
/* eslint-env browser,jasmine */
|
/* eslint-env browser,jasmine */
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
var Y = require('../SpecHelper.js')
|
||||||
var numberOfYMapTests = 10
|
var numberOfYMapTests = 10
|
||||||
var repeatMapTeasts = 1
|
var repeatMapTeasts = 1
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
/* global Y */
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
;(function () {
|
module.exports = function (Y) {
|
||||||
class YTextBind extends Y.Array['class'] {
|
class YTextBind extends Y.Array['class'] {
|
||||||
constructor (os, _model, idArray, valArray) {
|
constructor (os, _model, idArray, valArray) {
|
||||||
super(os, _model, idArray, valArray)
|
super(os, _model, idArray, valArray)
|
||||||
@ -287,4 +286,4 @@
|
|||||||
return new YTextBind(os, model.id, idArray, valArray)
|
return new YTextBind(os, model.id, idArray, valArray)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})()
|
}
|
||||||
|
45
src/Utils.js
45
src/Utils.js
@ -1,4 +1,3 @@
|
|||||||
/* global Y */
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -21,7 +20,10 @@
|
|||||||
database request to finish). EventHandler will help you to make your type
|
database request to finish). EventHandler will help you to make your type
|
||||||
synchronously.
|
synchronously.
|
||||||
*/
|
*/
|
||||||
class EventHandler {
|
module.exports = function (Y) {
|
||||||
|
Y.utils = {}
|
||||||
|
|
||||||
|
class EventHandler {
|
||||||
/*
|
/*
|
||||||
onevent: is called when the structure changes.
|
onevent: is called when the structure changes.
|
||||||
|
|
||||||
@ -132,10 +134,10 @@ class EventHandler {
|
|||||||
this.onevent(events)
|
this.onevent(events)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Y.utils.EventHandler = EventHandler
|
Y.utils.EventHandler = EventHandler
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A wrapper for the definition of a custom type.
|
A wrapper for the definition of a custom type.
|
||||||
Every custom type must have three properties:
|
Every custom type must have three properties:
|
||||||
|
|
||||||
@ -145,8 +147,8 @@ Y.utils.EventHandler = EventHandler
|
|||||||
- Given a model, creates a custom type
|
- Given a model, creates a custom type
|
||||||
* class
|
* class
|
||||||
- the constructor of the custom type (e.g. in order to inherit from a type)
|
- the constructor of the custom type (e.g. in order to inherit from a type)
|
||||||
*/
|
*/
|
||||||
class CustomType { // eslint-disable-line
|
class CustomType { // eslint-disable-line
|
||||||
constructor (def) {
|
constructor (def) {
|
||||||
if (def.createType == null ||
|
if (def.createType == null ||
|
||||||
def.initType == null ||
|
def.initType == null ||
|
||||||
@ -158,31 +160,31 @@ class CustomType { // eslint-disable-line
|
|||||||
this.initType = def.initType
|
this.initType = def.initType
|
||||||
this.class = def.class
|
this.class = def.class
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Y.utils.CustomType = CustomType
|
Y.utils.CustomType = CustomType
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Make a flat copy of an object
|
Make a flat copy of an object
|
||||||
(just copy properties)
|
(just copy properties)
|
||||||
*/
|
*/
|
||||||
function copyObject (o) {
|
function copyObject (o) {
|
||||||
var c = {}
|
var c = {}
|
||||||
for (var key in o) {
|
for (var key in o) {
|
||||||
c[key] = o[key]
|
c[key] = o[key]
|
||||||
}
|
}
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
Y.utils.copyObject = copyObject
|
Y.utils.copyObject = copyObject
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Defines a smaller relation on Id's
|
Defines a smaller relation on Id's
|
||||||
*/
|
*/
|
||||||
function smaller (a, b) {
|
function smaller (a, b) {
|
||||||
return a[0] < b[0] || (a[0] === b[0] && a[1] < b[1])
|
return a[0] < b[0] || (a[0] === b[0] && a[1] < b[1])
|
||||||
}
|
}
|
||||||
Y.utils.smaller = smaller
|
Y.utils.smaller = smaller
|
||||||
|
|
||||||
function compareIds (id1, id2) {
|
function compareIds (id1, id2) {
|
||||||
if (id1 == null || id2 == null) {
|
if (id1 == null || id2 == null) {
|
||||||
if (id1 == null && id2 == null) {
|
if (id1 == null && id2 == null) {
|
||||||
return true
|
return true
|
||||||
@ -194,5 +196,6 @@ function compareIds (id1, id2) {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Y.utils.compareIds = compareIds
|
||||||
}
|
}
|
||||||
Y.utils.compareIds = compareIds
|
|
||||||
|
63
src/y.js
63
src/y.js
@ -1,7 +1,63 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
|
require('./Connector.js')(Y)
|
||||||
|
require('./Database.js')(Y)
|
||||||
|
require('./Transaction.js')(Y)
|
||||||
|
require('./Struct.js')(Y)
|
||||||
|
require('./Utils.js')(Y)
|
||||||
|
require('./Databases/RedBlackTree.js')(Y)
|
||||||
|
require('./Databases/Memory.js')(Y)
|
||||||
|
require('./Databases/IndexedDB.js')(Y)
|
||||||
|
require('./Connectors/Test.js')(Y)
|
||||||
|
|
||||||
|
var requiringModules = {}
|
||||||
|
|
||||||
|
module.exports = Y
|
||||||
|
|
||||||
|
Y.extend = function (name, value) {
|
||||||
|
Y[name] = value
|
||||||
|
var resolves = requiringModules[name]
|
||||||
|
if (requiringModules[name] != null) {
|
||||||
|
for (var i = 0; i < resolves.length; i++) {
|
||||||
|
resolves[i]()
|
||||||
|
}
|
||||||
|
delete requiringModules[name]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
require('./Types/Array.js')(Y)
|
||||||
|
require('./Types/Map.js')(Y)
|
||||||
|
require('./Types/TextBind.js')(Y)
|
||||||
|
|
||||||
function Y (opts) {
|
function Y (opts) {
|
||||||
|
opts.types = opts.types != null ? opts.types : []
|
||||||
|
var modules = [opts.db.name, opts.connector.name].concat(opts.types)
|
||||||
|
var promises = []
|
||||||
|
for (var i = 0; i < modules.length; i++) {
|
||||||
|
if (Y[modules[i]] == null) {
|
||||||
|
try {
|
||||||
|
require(modules[i])(Y)
|
||||||
|
} catch (e) {
|
||||||
|
// module does not exist
|
||||||
|
if (window != null) {
|
||||||
|
if (requiringModules[modules[i]] == null) {
|
||||||
|
var imported = document.createElement('script')
|
||||||
|
var name = modules[i].toLowerCase()
|
||||||
|
imported.src = opts.sourceDir + '/y-' + name + '/y-' + name + '.js'
|
||||||
|
document.head.appendChild(imported)
|
||||||
|
requiringModules[modules[i]] = []
|
||||||
|
}
|
||||||
|
promises.push(new Promise(function (resolve) {
|
||||||
|
requiringModules[modules[i]].push(resolve)
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.all(promises).then(function () {
|
||||||
return new Promise(function (resolve) {
|
return new Promise(function (resolve) {
|
||||||
var yconfig = new YConfig(opts, function () {
|
var yconfig = new YConfig(opts, function () {
|
||||||
yconfig.db.whenUserIdSet(function () {
|
yconfig.db.whenUserIdSet(function () {
|
||||||
@ -9,6 +65,7 @@ function Y (opts) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
class YConfig {
|
class YConfig {
|
||||||
@ -49,9 +106,3 @@ class YConfig {
|
|||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
window.Y = Y
|
window.Y = Y
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof YConcurrency_TestingMode !== 'undefined') {
|
|
||||||
g.Y = Y //eslint-disable-line
|
|
||||||
// debugger //eslint-disable-line
|
|
||||||
}
|
|
||||||
Y.utils = {}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user