path bug fixes & NTS

This commit is contained in:
Mark Nadal 2015-10-15 16:34:30 -07:00
parent 0239023e92
commit 2c80ae81e9
4 changed files with 90 additions and 115 deletions

34
gun.js
View File

@ -132,7 +132,7 @@
Gun.is.graph(graph, function(node, ss){ Gun.is.graph(graph, function(node, ss){
c += 1; s = ss; c += 1; s = ss;
Gun.HAM(vertex, node, function(){}, function(vertex, field, value){ Gun.HAM(vertex, node, function(){}, function(vertex, field, value){
(vertex._[Gun._.HAM] = vertex._[Gun._.HAM] || {})[field] = node._[Gun._.HAM][field]; (vertex._[Gun._.HAM] = vertex._[Gun._.HAM] || {})[field] = (node._[Gun._.HAM] = node._[Gun._.HAM] || {})[field];
vertex[field] = value; vertex[field] = value;
}, function(){}); }, function(){});
}); });
@ -489,7 +489,6 @@
var chain = this || gun, src = opt.src || gun; var chain = this || gun, src = opt.src || gun;
var ctx = {path: path.split('.')}, field = Gun.text.ify(ctx.path.shift()); var ctx = {path: path.split('.')}, field = Gun.text.ify(ctx.path.shift());
var val = node[field], soul = Gun.is.soul(val); var val = node[field], soul = Gun.is.soul(val);
console.log("chain.path", field, node, '\n');
if(!field && !ctx.path.length){ if(!field && !ctx.path.length){
cb.call(chain, null, node, field); cb.call(chain, null, node, field);
return opt.step? src._.at('soul').emit({soul: $.soul, field: null, from: opt.step.soul, at: opt.step.field, gun: chain, PATH: 'SOUL'}) return opt.step? src._.at('soul').emit({soul: $.soul, field: null, from: opt.step.soul, at: opt.step.field, gun: chain, PATH: 'SOUL'})
@ -497,10 +496,9 @@
} }
if(!Gun.obj.has(node, field)){ if(!Gun.obj.has(node, field)){
if(opt.end || (!ctx.path.length && gun.__.meta($.soul).end)){ // TODO: Make it so you can adjust how many terminations! if(opt.end || (!ctx.path.length && gun.__.meta($.soul).end)){ // TODO: Make it so you can adjust how many terminations!
console.log("whap!"); // TODO: BUG! `chain` here is incorrect on unknowns. This has been fixed at an API level.
// TODO: BUG! `chain` here is incorrect on unknowns.
cb.call(chain, null, null, field); cb.call(chain, null, null, field);
src._.at('soul').emit({soul: $.soul, field: field, gun: chain, PATH: 'SOUL'}); src._.at('null').emit({soul: $.soul, field: field, gun: chain, PATH: 'NULL'});
} }
return; return;
} }
@ -590,18 +588,18 @@
If this causes any application-level concern, it can compare against the live data by immediately reading it, or accessing the logs if enabled. If this causes any application-level concern, it can compare against the live data by immediately reading it, or accessing the logs if enabled.
*/ */
Chain.put = function(val, cb, opt){ // TODO: handle case where val is a gun context! Chain.put = function(val, cb, opt){ // TODO: handle case where val is a gun context!
var gun = this.chain(), call = function(){ var gun = this.chain(), call = function($){
gun.back._.at('soul').emit({soul: Gun.is.soul.on(val) || Gun.roulette.call(gun), empty: true, PUT: 'SOUL'}); // TODO: refactor Gun.roulette! gun.back._.at('soul').emit({soul: $.soul || Gun.is.soul.on(val) || Gun.roulette.call(gun), field: $.field, empty: true, gun: $.gun, PUT: 'SOUL'}); // TODO: refactor Gun.roulette!
}, drift = Gun.time.now(); // TODO: every instance of gun maybe should have their own version of time. }, drift = Gun.time.now(); // TODO: every instance of gun maybe should have their own version of time.
cb = cb || function(){}; cb = cb || function(){};
opt = opt || {}; opt = opt || {};
if(!gun.back.back){ if(!gun.back.back){
gun = gun.chain(); gun = gun.chain();
call(); call({});
} }
if(gun.back.not){ gun.back.not(call) } 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').once(function($){ // TODO: maybe once per soul?
var chain = $.gun || gun; var chain = $.gun || gun;
var ctx = {}, obj = val, $ = Gun.obj.copy($); var ctx = {}, obj = val, $ = Gun.obj.copy($);
console.log("chain.put", val, '\n'); console.log("chain.put", val, '\n');
@ -644,7 +642,7 @@
env.graph[at.node._[Gun._.soul] = at.soul] = at.node; env.graph[at.node._[Gun._.soul] = at.soul] = at.node;
cb(at, at.soul); cb(at, at.soul);
}; };
$.empty? path() : gun.back.path(at.path, path, {once: true, end: true}); // TODO: clean this up. ($.empty && !$.field)? path() : chain.back.path(at.path, path, {once: true, end: true}); // TODO: clean this up.
} }
} }
if(!at.node._[Gun._.HAM]){ if(!at.node._[Gun._.HAM]){
@ -671,6 +669,7 @@
}); });
} }
}); });
return gun; return gun;
} }
Chain.map = function(cb, opt){ Chain.map = function(cb, opt){
@ -711,19 +710,22 @@
obj[index] = val; obj[index] = val;
return Gun.is.value(val)? gun.put(obj, cb) : gun.put(obj, cb).path(index); return Gun.is.value(val)? gun.put(obj, cb) : gun.put(obj, cb).path(index);
} }
Chain.not = function(cb){ Chain.not = function(cb, opt){
var gun = this, ctx = {}; var gun = this, ctx = {};
cb = cb || function(){}; cb = cb || function(){};
opt = opt || {};
gun._.at('null').once(function(key){ gun._.at('null').once(function($){
if(key.soul || gun.__.key.s[key = (key || {}).key]){ return } if($.key && ($.soul || gun.__.key.s[$.key])){ return }
if($.field && Gun.obj.has(gun.__.graph[$.soul], $.field)){ return }
// TODO! BUG? Removed a start flag check and tests passed, but is that an edge case? // TODO! BUG? Removed a start flag check and tests passed, but is that an edge case?
var kick = function(next){ var kick = function(next){
if(++c){ return Gun.log("Warning! Multiple `not` resumes!"); } if(++c){ return Gun.log("Warning! Multiple `not` resumes!"); }
next._.at('soul').once(function($){ $.N0T = 'KICK SOUL'; gun._.at('soul').emit($) }); next._.at('soul').once(function($){ $.N0T = 'KICK SOUL'; gun._.at('soul').emit($) });
}, chain = gun.chain(), next = cb.call(chain, key, kick), c = -1; }, chain = gun.chain(), next = cb.call(chain, opt.raw? $ : ($.field || $.key), kick), c = -1;
if(Gun.is(next)){ kick(next) } if(Gun.is(next)){ kick(next) }
chain._.at('soul').emit({soul: Gun.roulette.call(chain), empty: true, key: key, N0T: 'SOUL', WAS: 'ON'}); // WAS ON! TOOD: refactor Gun.roulette gun.__.graph[kick.soul = Gun.roulette.call(chain)] = gun.__.graph[kick.soul] || Gun.union.pseudo(kick.soul); // TODO: refactor Gun.roulette
chain._.at('soul').emit({soul: kick.soul, empty: true, key: $.key, field: $.field, N0T: 'SOUL', WAS: 'ON'}); // WAS ON!
}); });
return gun; return gun;

View File

@ -1,23 +1,23 @@
Gun.on('opt').event(function(gun, opt){ Gun.on('opt').event(function(gun, opt){
if(!Gun.request){ return } if(!gun.tab || !gun.tab.request){ return }
var objectiveTimeOffset = 0; Gun.time.now.drift = 0;
Gun.obj.map(opt.peers || gun.__.opt.peers, function(peer, url){ function ping(){
var NTS = {}; Gun.obj.map(opt.peers || gun.__.opt.peers, function(peer, url){
NTS.start = Gun.time.is(); var NTS = {};
console.log(url + '.nts'); NTS.start = Gun.time.now();
Gun.request(url + '.nts', null, function(err, reply){ gun.tab.request(url, null, function(err, reply){
console.log("reply", err, reply); if(err || !reply || !reply.body){
if(err || !reply || !reply.body){ return console.log("Network Time Synchronization not supported", err, (reply || {}).body);
return console.log("Network Time Synchronization not supported", err, (reply || {}).body); }
} NTS.end = Gun.time.now();
NTS.end = Gun.time.is(); NTS.latency = (NTS.end - NTS.start)/2;
NTS.latency = (NTS.end - NTS.start)/2; if(!Gun.obj.has(reply.body, 'time')){ return }
if(Gun.obj.has(reply.body, 'time')){ return } NTS.calc = NTS.latency + reply.body.time;
NTS.calc = reply.body.time + NTS.latency; Gun.time.now.drift -= (NTS.end - NTS.calc)/2;
objectiveTimeOffset += (objectiveTimeOffset - NTS.calc)/2; setTimeout(ping, 250);
console.log('NTS', NTS.latency, NTS.calc, objectiveTimeOffset); }, {url: {pathname: '.nts'}});
}, {}); });
}); }; ping();
}); });
// You need to figure out how to make me write tests for this! // You need to figure out how to make me write tests for this!
// maybe we can do human based testing where we load a HTML that just // maybe we can do human based testing where we load a HTML that just

View File

@ -102,12 +102,11 @@
var key = req.url.key var key = req.url.key
, reply = {headers: {'Content-Type': tran.json}}; , reply = {headers: {'Content-Type': tran.json}};
//console.log(req); //console.log(req);
/* NTS HACK! SHOULD BE ITS OWN ISOLATED MODULE! /* NTS HACK! SHOULD BE ITS OWN ISOLATED MODULE! */
if(req && req.url && req.url.pathname && req.url.pathname.indexOf('gun.nts') >= 0){ if(req && req.url && req.url.pathname && req.url.pathname.indexOf('gun.nts') >= 0){
console.log("GOT IT");
return cb({headers: reply.headers, body: {time: Gun.time.is() }}); return cb({headers: reply.headers, body: {time: Gun.time.is() }});
} }
NTS END! SHOULD HAVE BEEN ITS OWN MODULE */ /* NTS END! SHOULD HAVE BEEN ITS OWN MODULE */
if(req && req.url && Gun.obj.has(req.url.query, '*')){ if(req && req.url && Gun.obj.has(req.url.query, '*')){
return gun.all(req.url.key + req.url.search, function(err, list){ return gun.all(req.url.key + req.url.search, function(err, list){
cb({headers: reply.headers, body: (err? (err.err? err : {err: err || "Unknown error."}) : list || null ) }) cb({headers: reply.headers, body: (err? (err.err? err : {err: err || "Unknown error."}) : list || null ) })
@ -161,32 +160,6 @@
}); });
}).err){ cb({headers: reply.headers, body: {err: req.err || "Union failed."}}) } }).err){ cb({headers: reply.headers, body: {err: req.err || "Union failed."}}) }
} }
return;
//return;
// saving
Gun.obj.map(req.body, function(node, soul){ // iterate over every node
if(soul != Gun.is.soul.on(node)){ return this.end("No soul!") }
gun.get({'#': soul}, this.add(soul)); // and begin getting it in case it is not cached.
}, Gun.fns.sum(function(err){
if(err){ return reply.err = err }
reply.got = true;
}));
// could there be a timing error somewhere in here?
var setImmediate = setImmediate || setTimeout; // TODO: BUG: This should be cleaned up, but I want Heroku to work.
setImmediate(function(){ // do not do it right away because gun.get above is async, this should be cleaner.
var context = Gun.union(gun, req.body, function check(err, ctx){ // check if the body is valid, and get it into cache immediately.
context = ctx || context;
if(err || reply.err || context.err || !context.nodes){ return cb({headers: reply.headers, body: {err: err || reply.err || context.err || "Union failed." }}) }
if(!Gun.fns.is(gun.__.opt.hooks.put)){ return cb({headers: reply.headers, body: {err: "Persistence not supported." }}) }
if(!reply.got){ return setTimeout(check, 2) } // only persist if all nodes are in cache.
gun.__.opt.hooks.put(context.nodes, function(err, data){ // since we've already manually done the union, we can now directly call the persistence layer.
if(err){ return cb({headers: reply.headers, body: {err: err || "Persistence failed." }}) }
cb({headers: reply.headers, body: {ok: "Persisted."}}); // TODO: Fix so we know what the reply is.
});
});
}, 0);
gun.server.on('network').emit(req);
} }
tran.put.key = function(req, cb){ // key hook! tran.put.key = function(req, cb){ // key hook!
if(!req || !req.url || !req.url.key || !Gun.obj.has(req.body, Gun._.soul)){ return } if(!req || !req.url || !req.url.key || !Gun.obj.has(req.body, Gun._.soul)){ return }

View File

@ -10,7 +10,8 @@ describe('Gun', function(){
describe('Utility', function(){ describe('Utility', function(){
it('verbose console.log debugging', function(done) { console.log("TURN THIS BACK ON the DEBUGGING TEST"); done(); return; /* // causes logger to no longer log.
it('verbose console.log debugging', function(done) {
var gun = Gun(); var gun = Gun();
var log = root.console.log, counter = 1; var log = root.console.log, counter = 1;
@ -31,6 +32,7 @@ describe('Gun', function(){
}); });
}); });
}); });
*/
describe('Type Check', function(){ describe('Type Check', function(){
it('binary', function(){ it('binary', function(){
@ -1408,7 +1410,7 @@ describe('Gun', function(){
}); });
it('val path put val key', function(done){ // bug discovered from Jose's visualizer // TODO: still timing issues, 0.6! it('val path put val key', function(done){ // bug discovered from Jose's visualizer
var gun = Gun(), s = Gun.time.is(), n = function(){ return Gun.time.is() } var gun = Gun(), s = Gun.time.is(), n = function(){ return Gun.time.is() }
this.timeout(5000); this.timeout(5000);
@ -1417,8 +1419,8 @@ describe('Gun', function(){
gun.get('user/alfred').val(function(a){ gun.get('user/alfred').val(function(a){
gun.get('user/beth').path('friend').put(a); // b - friend_of -> 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){ // TODO: We should have b.friend by now!
gun.get('user/alfred').path('friend').put(b, function(){ // a - friend_of -> 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"}, function(err, ok){ gun.get('user/beth').path('cat').put({name: "fluffy", age: 3, coat: "tabby"}).val(function(cat){
gun.get('user/alfred').path('friend.cat').key('the/cat'); gun.get('user/alfred').path('friend.cat').key('the/cat');
@ -1837,36 +1839,26 @@ describe('Gun', function(){
}) })
}); });
it.skip("gun get empty set, path val -> this put", function(done){ // Issue #99 #101, bug in survey and trace game. 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 test = {c: 0}, u;
var gun = Gun(); var gun = Gun();
console.log("game = gun get GAME set");
var game = gun.get('some/not/yet/set/put/thing').set(); var game = gun.get('some/not/yet/set/put/thing').set();
console.log("me = game data path ALIAS val"); var me = game.path('alias').val(function(val){
// TODO: add one for NOT
// the behavior we decided is VAL will hang until data defined
// NOT will get called after first peer (or configurable).
var me = game.path('alias').on(function(val){
console.log("TESTING!!!!", val);
expect(val).to.not.be(u); expect(val).to.not.be(u);
expect(val.a).to.be('b');
var meid = Gun.is.soul.on(val); var meid = Gun.is.soul.on(val);
var self = this; var self = this;
expect(self === game).to.not.be.ok(); expect(self === game).to.not.be.ok();
expect(self === me).to.be.ok(); expect(self === me).to.be.ok();
if(test.c++){ return } done();
self.put({x: 0, y: 0}); });
setTimeout(function(){ setTimeout(function(){
var graph = Gun.obj.copy(game.__.graph); me.put({a: 'b'});
Gun.obj.map(graph, function(node){delete node._}); });
},100);
})
}); });
// gr.put({a: {b: {c: 'd'}}}).path('z').set().val(); TEST sets on paths. it("gun get empty set path empty later path put multi", function(done){ // Issue #99 #101, bug in survey and trace game.
done.c = 0;
it.skip("gun get empty set path empty later path put multi", function(done){ // Issue #99 #101, bug in survey and trace game.
Gun.log.verbose = true;
var gun = Gun(); var gun = Gun();
var data = gun.get('some/not/yet/set/put/thing/2').set(); var data = gun.get('some/not/yet/set/put/thing/2').set();
var path = data.path('sub'); var path = data.path('sub');
@ -1874,23 +1866,15 @@ describe('Gun', function(){
setTimeout(function(){ setTimeout(function(){
path.put(d, function(err, ok){ path.put(d, function(err, ok){
expect(err).to.not.be.ok(); expect(err).to.not.be.ok();
if(f){ done.c++;
path.val(function(v){ if(f && done.c >= 3){
delete v._; done();
expect(v).to.eql(d);
done();
});
} }
}); });
setTimeout(function(){
data.val(function(v){
console.log(d, "DATA", data.__.graph);
});
},2)
},t || 10); },t || 10);
}; };
put({on: 'bus', not: 'transparent'}); put({on: 'bus', not: 'transparent'});
put({on: null, not: 'torrent'}, 100); put({on: null, not: 'torrent'}, 200);
put({on: 'sub', not: 'parent'}, 250, true); put({on: 'sub', not: 'parent'}, 250, true);
}); });
@ -1943,8 +1927,8 @@ describe('Gun', function(){
done.c = 0; done.c = 0;
var u; var u;
var gun = Gun(); var gun = Gun();
var game = gun.get('players').set(); var game = gun.get('game1/players').set();
var me = game.path('player3').val(function(val){ var me = game.path('player1').val(function(val){
if(!done.c){ done.fail = true } if(!done.c){ done.fail = true }
expect(val).to.not.be(u); expect(val).to.not.be(u);
expect(val.x).to.be(0); expect(val.x).to.be(0);
@ -1956,35 +1940,51 @@ describe('Gun', function(){
done.c++; done.c++;
expect(done.fail).to.not.be.ok(); expect(done.fail).to.not.be.ok();
me.put({x: 0, y: 0}); me.put({x: 0, y: 0});
},10) },10);
}); });
it.only("gun get path empty on", function(done){ it("gun get path empty on", function(done){
done.c = 0; done.c = 0;
var u; var u;
var gun = Gun(); var gun = Gun();
var game = gun.get('players').set(); var game = gun.get('game2/players').set();
var me = game.path('player3').on(function(val){ var me = game.path('player2').on(function(val){
if(!done.c){ done.fail = true } if(!done.c){ done.fail = true }
expect(val).to.not.be(u); expect(val).to.not.be(u);
expect(val.x).to.be(0); expect(val.x).to.be(1);
expect(val.y).to.be(0); expect(val.y).to.be(1);
expect(done.fail).to.not.be.ok(); 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();
done.c++;
}); });
setTimeout(function(){ setTimeout(function(){
done.c++; done.c++;
expect(done.fail).to.not.be.ok(); expect(done.fail).to.not.be.ok();
me.put({x: 0, y: 0}); me.put({x: 1, y: 1});
},10) },10);
}); });
it.skip("gun get path empty not", function(done){ it("gun get path empty not", function(done){
var g = Gun(); var u;
var gun = Gun();
var game = gun.get('game3/players').set();
var me = game.path('player3').not(function(field){
expect(field).to.be('player3');
done();
});
}); });
it.skip("gun get path empty set", function(done){ it("gun get path empty set", function(done){
var g = Gun(); var u;
var gun = Gun();
var game = gun.get('game4/players').set();
var me = game.path('player4').set().path('alias').put('awesome').val(function(val, field){
expect(val).to.be('awesome');
expect(field).to.be('alias');
done();
})
}); });
}); });