Skip to content

Instantly share code, notes, and snippets.

@tmpvar
Last active August 29, 2015 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tmpvar/2ef32d3ecb8e040b3628 to your computer and use it in GitHub Desktop.
Save tmpvar/2ef32d3ecb8e040b3628 to your computer and use it in GitHub Desktop.
in-memory datastore with rewind/forward support
function Timestep() {
this.start = Date.now();
this.ops = [];
this.store = {};
this.index = -1;
}
Timestep.prototype.op = function(type, path, value, store) {
var oldValue = null;
var loc = this.path(path);
var time = Date.now();
switch (type) {
case '~':
oldValue = loc[0][loc[1]];
case '+':
if (Array.isArray(loc[0])) {
if (!loc[1].length) {
loc[0].push(value)
} else {
var index = parseInt(loc[1]);
if (!isNaN(index)) {
loc[0].splice(index, 1, value);
}
}
} else {
loc[0][loc[1]] = value;
}
break;
case '-':
if (Array.isArray(loc[0])) {
var index = parseInt(loc[1]);
if (!isNaN(index)) {
loc[0].splice(index, 1);
} else {
loc[0].pop();
}
} else {
oldValue = loc[0][loc[1]];
delete loc[0][loc[1]];
}
break;
}
if (store !== false) {
if (this.index < this.ops.length - 1) {
this.ops.splice(this.index+1, this.ops.length);
}
this.ops.push([
time,
path,
type,
value,
oldValue
]);
this.index++;
}
return this;
};
Timestep.prototype.rewind = function(steps) {
if (!steps) {
steps = this.index;
}
for (var i = 0; i<steps; i++) {
var c = this.ops[this.index];
if (!c) {
break;
}
switch (c[2]) {
case '+':
this.op('-', c[1], null, false);
break;
case '~':
case '-':
this.op('+', c[1], c[4], false);
break;
}
this.index--;
}
}
Timestep.prototype.forward = function(steps) {
if (!steps) {
steps = (this.ops.length - this.index);
}
for (var i = 0; i<steps; i++) {
this.index++;
var c = this.ops[this.index];
if (!c) {
this.index--;
break;
}
this.op(c[2], c[1], c[3], false)
}
};
Timestep.prototype.path = function(path) {
var parts = path.split('/');
var c = this.store;
while(parts.length-1) {
var component = parts.shift();
if (typeof c[component] !== 'undefined') {
c = c[component];
} else {
return;
}
}
parts.unshift(c);
return parts;
};
Timestep.prototype.val = function(path, value) {
var loc = this.path(path);
if (loc) {
return loc[0][loc[1]];
}
};
first [ 'aa', 'b', 'c' ]
rewind(5) undefined
forward(5) [ 'aa', 'b', 'c' ]
rewind(3) [ 'a' ]
forward() [ 'aa', 'b', 'c' ]
rewind() []
forward() [ 'aa', 'b', 'c' ]
{ start: 1399963068987,
ops:
[ [ 1399963068987, 'something', '+', [Object], null ],
[ 1399963068987, 'something/', '+', 'a', null ],
[ 1399963068987, 'something/', '+', 'b', null ],
[ 1399963068987, 'something/', '+', 'c', null ],
[ 1399963068987, 'something/0', '~', 'aa', 'a' ] ],
store: { something: [ 'aa', 'b', 'c' ] },
index: 4 }
[ '0', 'b', 'c' ]
{ start: 1399963068987,
ops:
[ [ 1399963068987, 'something', '+', [Object], null ],
[ 1399963068987, 'something/', '+', 'a', null ],
[ 1399963068987, 'something/', '+', 'b', null ],
[ 1399963068987, 'something/', '+', 'c', null ],
[ 1399963068989, 'something/0', '~', '0', 'a' ] ],
store: { something: [ '0', 'b', 'c' ] },
index: 4 }
var ts = new Timestep();
ts.op('+', 'something', []);
ts.op('+', 'something/', 'a');
ts.op('+', 'something/', 'b');
ts.op('+', 'something/', 'c');
ts.op('~', 'something/0', 'aa');
console.log('first', ts.val('something'));
ts.rewind(5);
console.log('rewind(5)', ts.val('something'));
ts.forward(5);
console.log('forward(5)', ts.val('something'));
ts.rewind(3);
console.log('rewind(3)', ts.val('something'));
ts.forward();
console.log('forward()', ts.val('something'));
ts.rewind()
console.log('rewind()', ts.val('something'));
ts.forward()
console.log('forward()', ts.val('something'));
console.log(ts);
ts.rewind(1);
ts.op('~', 'something/0', '0');
console.log(ts.val('something'));
console.log(ts);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment