fix Jesse's synchronous Trace bug

This commit is contained in:
Mark Nadal 2015-11-02 16:41:01 -08:00
parent 7dee2d8b92
commit 272935630b
5 changed files with 158 additions and 40 deletions

View File

@ -4,4 +4,7 @@ node_js:
- 0.8
- 0.10
- 0.11
- 0.12
- 0.12
- 4.0
- 4.2
- 5.0

48
gun.js
View File

@ -478,7 +478,6 @@
opt = opt || {};
if(!Gun.text.is(path = Gun.text.is(path)? path || null : Gun.num.is(path)? (path + '') : Gun.list.is(path)? path.join('.') : path)){ return cb.call(gun, {err: Gun.log("Invalid path '" + path + "'!")}), gun }
if(!gun.back._.at){ return cb.call(gun, {err: Gun.log("No context!")}), gun }
gun.back.on(function($, node){
if(!(node = node || gun.__.graph[$.soul])){ return }
var chain = this || gun, src = opt.src || gun;
@ -500,11 +499,11 @@
if(soul){
return gun.get(val, function(err, data){
if(err){ return cb.call(chain, err) }
}).path(ctx.path, cb, {src: src, step: {soul: $.soul, field: field}});
}).path(ctx.path, cb, {src: src, step: {soul: $.soul, field: field}, path: path});
}
cb.call(chain, null, val, field);
return src._.at('soul').emit({soul: $.soul, field: field, gun: chain, PATH: 'SOUL'});
}, {raw: true, once: true});
}, {raw: true});
return gun;
}
@ -520,29 +519,38 @@
opt = opt || {};
gun.on(function($, delta, on){
var node = gun.__.graph[$.soul], hash = $.soul + ($.field || '');
if(!$.soul || !node || ctx[hash + '.end']){ return }
var node = gun.__.graph[$.soul];
if(ctx[$.soul + '.end']){ return ctx[$.soul + '.end'](node, $) }
//(on = on || {off:function(){}}).off();
ctx[hash + '.end'] = function(data){
if(data && $.soul != Gun.is.soul.on(data)){ return }
var node = gun.__.graph[$.soul] || node; //on = (this || {off:function(){}}); // TODO: BUG? is var node = thing || node safe in old IE?
if($.key){
ctx[$.soul + '.end'] = function(data, $$){
$$ = $$ || $;
var soul, field;
if(!$$.field && $$.from){ // if the current node is a child of the parent that we were subscribing to a field on.
soul = $$.from;
field = $$.at;
} else {
soul = $$.soul;
field = $$.field || '';
}
var hash = soul + field;
var node = gun.__.graph[$$.soul] || data || node; //on = (this || {off:function(){}}); // TODO: BUG? is var node = thing || node safe in old IE?
if($$.key){
// TODO: BUG! Shouldn't `.val` pseudo union check that each node in the key graph is ended? Current thought: Not necessarily! Since `.val` is first come first serve until we provide configurable end options.
node = Gun.union.pseudo($.key, gun.__.key.s[$.key]) || node;
}
if($.field){
if(!Gun.obj.has(node, $.field) || ctx[hash] || Gun.is.soul(node[$.field])){ return }
if($$.field){
if(!Gun.obj.has(node, $$.field) || ctx[hash] || Gun.is.soul(node[$$.field])){ return }
ctx[hash] = true; //on.off(); // TODO: Fix the bug with at for this to be on.
return cb.call($.gun || gun, node[$.field], $.field || $.at);
return cb.call($$.gun || gun, node[$$.field], $$.field);
}
if(!gun.__.meta($.soul).end || (ctx[$.soul] || ($.key && ctx[$.key]))){ return } // TODO: Add opt to change number of terminations.
ctx[$.soul] = ctx[$.key] = true; //on.off(); // TODO: Fix the bug with at for this to be on.
cb.call($.gun || gun, Gun.obj.copy(node), $.field || $.at);
if(!gun.__.meta($$.soul).end || (ctx[hash] || ($$.key && ctx[$$.key]))){ return } // TODO: Add opt to change number of terminations.
ctx[hash] = ctx[$$.soul] = ctx[$$.key] = true; //on.off(); // TODO: Fix the bug with at for this to be on.
cb.call($$.gun || gun, Gun.obj.copy(node), field);
}
if(gun.__.meta($.soul).end){
if(!$.field || Gun.obj.has(node, $.field)){ return ctx[hash + '.end']() }
if(!$.field || Gun.obj.has(node, $.field)){ return ctx[$.soul + '.end'](node, $) }
}
gun.__.on($.soul + '.end').event(ctx[hash + '.end']);
gun.__.on($.soul + '.end').event(ctx[$.soul + '.end']);
}, {raw: true});
return gun;
@ -604,9 +612,13 @@
}
if(gun.back.not){ gun.back.not(call, {raw: true}) }
gun.back._.at('soul').event(function($){ // TODO: maybe once per soul?
gun.back._.at('soul').event(function($){
var chain = $.gun || gun;
var ctx = {}, obj = val, $ = Gun.obj.copy($);
var hash = $.field? $.soul + $.field : ($.from? $.from + ($.at || '') : $.soul);
//var hash = $.from? ($.from + ($.at || '')) : ($.soul + ($.field || ''));
if(call[hash]){ return }
call[hash] = true;
console.log("chain.put", val, '\n');
if(Gun.is.value(obj)){
if($.from && $.at){

View File

@ -1,6 +1,6 @@
{
"name": "gun",
"version": "0.2.4",
"version": "0.2.5",
"description": "Graph engine",
"main": "index.js",
"scripts": {
@ -12,6 +12,8 @@
"url": "git+https://github.com/amark/gun.git"
},
"keywords": [
"gun",
"gunDB",
"graph",
"document",
"key",
@ -23,9 +25,9 @@
"realtime",
"decentralized",
"peer-to-peer",
"distributed",
"P2P",
"OSS",
"distributed",
"embedded",
"localstorage",
"S3"
@ -48,4 +50,4 @@
"devDependencies": {
"mocha": "~>1.9.0"
}
}
}

View File

@ -1005,9 +1005,10 @@ describe('Gun', function(){
it('get node path', function(done){
gun.get('hello/key').path('hi', function(err, val){
if(done.end){ return } // it is okay for path's callback to be called multiple times.
expect(err).to.not.be.ok();
expect(val).to.be('overwritten');
done();
done(); done.end = true;
});
});
@ -1215,6 +1216,20 @@ describe('Gun', function(){
});
});
it('Gun get put null', function(done){ // flip flop bug
var gun = Gun();
gun.put({last: {some: 'object'}}).path('last').val(function(val, field){
done.some = true;
//console.log("*******************************", field, val);
expect(val.some).to.be('object');
}).put(null).val(function(val, field){
//console.log("***************null****************", field, val);
expect(val).to.be(null);
expect(done.some).to.be.ok();
done();
});
});
it('var put key path', function(done){ // contexts should be able to be saved to a variable
var foo = gun.put({foo: 'bar'}).key('foo/bar');
foo.path('hello.world.nowhere'); // this should become a sub-context, that doesn't alter the original
@ -1430,7 +1445,7 @@ describe('Gun', function(){
gun.put({gender:'f', age:22, name:'beth' }).key('user/beth');
gun.get('user/alfred').val(function(a){
gun.get('user/beth').path('friend').put(a); // b - friend_of -> a
gun.get('user/beth').val(function(b){ // TODO: We should have b.friend by now!
gun.get('user/beth').val(function(b){
gun.get('user/alfred').path('friend').put(b).val(function(beth){ // a - friend_of -> b
gun.get('user/beth').path('cat').put({name: "fluffy", age: 3, coat: "tabby"}).val(function(cat){
@ -1584,7 +1599,7 @@ describe('Gun', function(){
var gun = Gun();
gun.get('set').set().set().val(function(val){
done.c += 1;
expect(Gun.obj.empty(val, '_')).to.be.ok();
expect(Gun.obj.empty(val, Gun._.meta)).to.be.ok();
setTimeout(function(){
expect(done.c).to.be(1);
done()
@ -1592,11 +1607,11 @@ describe('Gun', function(){
});
});
it('set multiple', function(done){
it('set multiple', function(done){ // kinda related to flip flop?
var gun = Gun().get('sets').set(), i = 0;
gun.val(function(val){
expect(done.soul = Gun.is.soul.on(val)).to.be.ok();
expect(Gun.obj.empty(val, '_')).to.be.ok();
expect(Gun.obj.empty(val, Gun._.meta)).to.be.ok();
});
gun.set(1).set(2).set(3).set(4); // if you set an object you'd have to do a `.back`
@ -1662,7 +1677,7 @@ describe('Gun', function(){
gun.put({b: 2, z: 0}).key('pseudon');
gun.get('pseudon').on(function(val){
if(done.val){ return } // TODO: Maybe prevent repeat ons where there is no diff?
if(done.val){ return } // TODO: Maybe prevent repeat ons where there is no diff? (may not happen to after 1.0.0)
done.val = val;
expect(val.a).to.be(1);
expect(val.b).to.be(2);
@ -1851,6 +1866,32 @@ describe('Gun', function(){
})
});
/* // TODO: BUG! BAD! THIS IS AN ACTIVE BUG THAT NEEDS TO BE FIXED!!!!
it('gun get put, sub path put, original val', function(done){ // bug from Jesse working on Trace
var gun = Gun().get('players');
gun.put({
taken: true,
history: {0: {}, 1: {}}
});
gun
.path('history')
.put(null)
.back
.path('taken')
.put(false)
// TODO: BUG! There is a variation of this, where we just do `.val` rather than `gun.val` and `.val` by itself (chained off of the sub-paths) doesn't even get called. :(
gun.val(function(players){ // this val is subscribed to the original put and therefore does not get any of the sub-path listeners, therefore it gets called EARLY with the original/old data rather than waiting for the sub-path data to "finish" and then get called.
console.log("LOOK HERE!!!!", players);
expect(players.taken).to.be(false);
expect(players.history).to.be(null);
done();
});
});
*/
it("gun get empty set, path not -> this put", function(done){ // Issue #99 #101, bug in survey and trace game.
var test = {c: 0}, u;
var gun = Gun();
@ -1919,10 +1960,11 @@ describe('Gun', function(){
var game = gun.put({board: null, teamA: null, teamB: null, turn: null}).key('the/game');
game.path('board').on(function(board, field){
expect(field).to.be('board');
if(done.c == 1){
if(done.c === 1){
expect(board).to.not.be.ok();
}
if(done.c === 2){
if(!board[11] || !board[22] || !board[33]){ return }
done.c++;
delete board._;
expect(board).to.be.eql({11: ' ', 22: ' ', 33: 'A'});
@ -1935,7 +1977,66 @@ describe('Gun', function(){
},100);
});
it("gun get path empty val", function(done){
it("get set put map -> put, foreach gun path map", function(done){ // replicate Jesse's Trace game bug
done.c = 0;
gun = Gun()
.get('players').set()
.put({
0: {
num: 0
},
1: {
num: 1
},
2: {
num: 2
},
3: {
num: 3
}
}, function(err,ok){
expect(done.c++).to.be(0);
}).val(function(p){
done.p = Gun.is.soul.on(p);
done.m = Gun.is.soul(p[0]);
expect(Gun.is.soul(p[0])).to.be.ok();
expect(Gun.is.soul(p[1])).to.be.ok();
expect(Gun.is.soul(p[2])).to.be.ok();
expect(Gun.is.soul(p[3])).to.be.ok();
})
var players = [], me;
gun.map(function (player, number) {
players[number] = player;
players[number].history = [];
if (!player.taken && !me) {
this.put({
taken: true,
history: {
0: {x: 1, y: 2}
}
}, function(err,ok){});
me = number;
}
});
([0, 1, 2, 3]).forEach(function (player, number) {
gun
.path(number + '.history')
.map(function (entry, logNum) {
done.c++;
players[number].history[logNum] = entry;
expect(entry.x).to.be(1);
expect(entry.y).to.be(2);
setTimeout(function(){
expect(done.c).to.be(2);
done();
},100);
});
});
});
it("gun get path empty val", function(done){ // flip flop bug
done.c = 0;
var u;
var gun = Gun();
@ -1959,17 +2060,17 @@ describe('Gun', function(){
done.c = 0;
var u;
var gun = Gun();
var game = gun.get('game2/players').set();
var game = gun.get('game2/players').set();
var me = game.path('player2').on(function(val){
if(!done.c){ done.fail = true }
expect(done.fail).to.not.be.ok();
expect(val).to.not.be(u);
if(done.done || !val.x || !val.y){ return } // it is okay if ON gets called many times, this protects against that.
// TODO: although it would be nice if we could minimize the amount of duplications. (may not happen to after 1.0.0)
expect(val.x).to.be(1);
expect(val.y).to.be(1);
expect(done.fail).to.not.be.ok();
if(done.c > 1){ return } // it is okay if ON gets called many times, this protects against that.
// although it would be nice if we could minimize the amount of duplications.
done.done = true;
done();
done.c++;
});
setTimeout(function(){
done.c++;
@ -1998,12 +2099,12 @@ describe('Gun', function(){
done();
})
});
});
});
describe('Streams', function(){
var gun = Gun(), g = function(){
return Gun({hooks: {get: ctx.get}});
}, ctx = {gen: 9, extra: 45, network: 2};
}, ctx = {gen: 9, extra: 100, network: 2};
it('prep hook', function(done){
this.timeout(ctx.gen * ctx.extra);

View File

@ -1,10 +1,10 @@
WIRE PROTOCOL
save/set/create/put/post/delete/update/change/mutate
put/save/set/create/post/delete/update/change/mutate
get/open/load/call/location/address
name/reference/key/index/point
key/name/reference/index/point
/gun {data: 'yay', #: "soul"}
@ -37,7 +37,7 @@ Query formats are allowed as:
Ex. "/users/?*=/&*>=a&*<=c" asks the peer to return users that start and end between 'a' and 'c',
thus {"users/alice": {"#": "DSAF"}, "users/bob": {"#": "DAFS"}, "user/carl": {"#": "SAFD"}}
# = ASDF
pound means the peer should reply with the node that has this exact soul. There should be no key prefixed path on this type of request. A special case of "#=*" indicates to literally dump the entire graph, as is, to the client. Lexical carets are okay.
pound means the peer should reply with a graph node that has this exact soul. There should be no key prefixed path on this type of request. A special case of "#=*" indicates to literally dump the entire graph, as is, to the client. Lexical carets are okay.
% = 30
percent means byte constraint requested by the peer asking for data.
> = 1426007247399