From 2c80ae81e9f0961dfa8c440b94e1db1e5918c406 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Thu, 15 Oct 2015 16:34:30 -0700 Subject: [PATCH] path bug fixes & NTS --- gun.js | 34 +++++++++-------- lib/nts.js | 38 +++++++++--------- lib/wsp.js | 31 +-------------- test/common.js | 102 ++++++++++++++++++++++++------------------------- 4 files changed, 90 insertions(+), 115 deletions(-) diff --git a/gun.js b/gun.js index 9d4a9da8..1ce4c304 100644 --- a/gun.js +++ b/gun.js @@ -132,7 +132,7 @@ Gun.is.graph(graph, function(node, ss){ c += 1; s = ss; 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; }, function(){}); }); @@ -489,7 +489,6 @@ var chain = this || gun, src = opt.src || gun; var ctx = {path: path.split('.')}, field = Gun.text.ify(ctx.path.shift()); var val = node[field], soul = Gun.is.soul(val); - console.log("chain.path", field, node, '\n'); if(!field && !ctx.path.length){ 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'}) @@ -497,10 +496,9 @@ } 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! - console.log("whap!"); - // TODO: BUG! `chain` here is incorrect on unknowns. + // TODO: BUG! `chain` here is incorrect on unknowns. This has been fixed at an API level. 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; } @@ -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. */ Chain.put = function(val, cb, opt){ // TODO: handle case where val is a gun context! - 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! + var gun = this.chain(), call = function($){ + 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. cb = cb || function(){}; opt = opt || {}; if(!gun.back.back){ 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 ctx = {}, obj = val, $ = Gun.obj.copy($); console.log("chain.put", val, '\n'); @@ -644,7 +642,7 @@ env.graph[at.node._[Gun._.soul] = at.soul] = at.node; 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]){ @@ -671,6 +669,7 @@ }); } }); + return gun; } Chain.map = function(cb, opt){ @@ -711,19 +710,22 @@ obj[index] = val; 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 = {}; cb = cb || function(){}; + opt = opt || {}; - gun._.at('null').once(function(key){ - if(key.soul || gun.__.key.s[key = (key || {}).key]){ return } + gun._.at('null').once(function($){ + 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? var kick = function(next){ if(++c){ return Gun.log("Warning! Multiple `not` resumes!"); } 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) } - 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; diff --git a/lib/nts.js b/lib/nts.js index b6c63dd8..9075debf 100644 --- a/lib/nts.js +++ b/lib/nts.js @@ -1,23 +1,23 @@ Gun.on('opt').event(function(gun, opt){ - if(!Gun.request){ return } - var objectiveTimeOffset = 0; - Gun.obj.map(opt.peers || gun.__.opt.peers, function(peer, url){ - var NTS = {}; - NTS.start = Gun.time.is(); - console.log(url + '.nts'); - Gun.request(url + '.nts', null, function(err, reply){ - console.log("reply", err, reply); - if(err || !reply || !reply.body){ - return console.log("Network Time Synchronization not supported", err, (reply || {}).body); - } - NTS.end = Gun.time.is(); - NTS.latency = (NTS.end - NTS.start)/2; - if(Gun.obj.has(reply.body, 'time')){ return } - NTS.calc = reply.body.time + NTS.latency; - objectiveTimeOffset += (objectiveTimeOffset - NTS.calc)/2; - console.log('NTS', NTS.latency, NTS.calc, objectiveTimeOffset); - }, {}); - }); + if(!gun.tab || !gun.tab.request){ return } + Gun.time.now.drift = 0; + function ping(){ + Gun.obj.map(opt.peers || gun.__.opt.peers, function(peer, url){ + var NTS = {}; + NTS.start = Gun.time.now(); + gun.tab.request(url, null, function(err, reply){ + if(err || !reply || !reply.body){ + return console.log("Network Time Synchronization not supported", err, (reply || {}).body); + } + NTS.end = Gun.time.now(); + NTS.latency = (NTS.end - NTS.start)/2; + if(!Gun.obj.has(reply.body, 'time')){ return } + NTS.calc = NTS.latency + reply.body.time; + Gun.time.now.drift -= (NTS.end - NTS.calc)/2; + setTimeout(ping, 250); + }, {url: {pathname: '.nts'}}); + }); + }; ping(); }); // 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 diff --git a/lib/wsp.js b/lib/wsp.js index 64377d6f..feb9787a 100644 --- a/lib/wsp.js +++ b/lib/wsp.js @@ -102,12 +102,11 @@ var key = req.url.key , reply = {headers: {'Content-Type': tran.json}}; //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){ - console.log("GOT IT"); 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, '*')){ 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 ) }) @@ -161,32 +160,6 @@ }); }).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! if(!req || !req.url || !req.url.key || !Gun.obj.has(req.body, Gun._.soul)){ return } diff --git a/test/common.js b/test/common.js index 60762ce7..0e37344b 100644 --- a/test/common.js +++ b/test/common.js @@ -9,8 +9,9 @@ describe('Gun', function(){ var t = {}; 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 log = root.console.log, counter = 1; @@ -31,6 +32,7 @@ describe('Gun', function(){ }); }); }); + */ describe('Type Check', 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() } this.timeout(5000); @@ -1417,8 +1419,8 @@ describe('Gun', function(){ 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/alfred').path('friend').put(b, function(){ // a - friend_of -> b - gun.get('user/beth').path('cat').put({name: "fluffy", age: 3, coat: "tabby"}, function(err, ok){ + 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){ 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 gun = Gun(); - console.log("game = gun get GAME set"); var game = gun.get('some/not/yet/set/put/thing').set(); - console.log("me = game data path ALIAS 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); + var me = game.path('alias').val(function(val){ expect(val).to.not.be(u); + expect(val.a).to.be('b'); var meid = Gun.is.soul.on(val); var self = this; expect(self === game).to.not.be.ok(); expect(self === me).to.be.ok(); - if(test.c++){ return } - self.put({x: 0, y: 0}); - setTimeout(function(){ - var graph = Gun.obj.copy(game.__.graph); - Gun.obj.map(graph, function(node){delete node._}); - - },100); - }) + done(); + }); + setTimeout(function(){ + me.put({a: 'b'}); + }); }); - // gr.put({a: {b: {c: 'd'}}}).path('z').set().val(); TEST sets on paths. - - 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; + 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; var gun = Gun(); var data = gun.get('some/not/yet/set/put/thing/2').set(); var path = data.path('sub'); @@ -1874,23 +1866,15 @@ describe('Gun', function(){ setTimeout(function(){ path.put(d, function(err, ok){ expect(err).to.not.be.ok(); - if(f){ - path.val(function(v){ - delete v._; - expect(v).to.eql(d); - done(); - }); + done.c++; + if(f && done.c >= 3){ + done(); } }); - setTimeout(function(){ - data.val(function(v){ - console.log(d, "DATA", data.__.graph); - }); - },2) },t || 10); }; put({on: 'bus', not: 'transparent'}); - put({on: null, not: 'torrent'}, 100); + put({on: null, not: 'torrent'}, 200); put({on: 'sub', not: 'parent'}, 250, true); }); @@ -1943,8 +1927,8 @@ describe('Gun', function(){ done.c = 0; var u; var gun = Gun(); - var game = gun.get('players').set(); - var me = game.path('player3').val(function(val){ + var game = gun.get('game1/players').set(); + var me = game.path('player1').val(function(val){ if(!done.c){ done.fail = true } expect(val).to.not.be(u); expect(val.x).to.be(0); @@ -1956,35 +1940,51 @@ describe('Gun', function(){ done.c++; expect(done.fail).to.not.be.ok(); 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; var u; var gun = Gun(); - var game = gun.get('players').set(); - var me = game.path('player3').on(function(val){ + var game = gun.get('game2/players').set(); + var me = game.path('player2').on(function(val){ if(!done.c){ done.fail = true } expect(val).to.not.be(u); - expect(val.x).to.be(0); - expect(val.y).to.be(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.c++; }); setTimeout(function(){ done.c++; expect(done.fail).to.not.be.ok(); - me.put({x: 0, y: 0}); - },10) + me.put({x: 1, y: 1}); + },10); }); - it.skip("gun get path empty not", function(done){ - var g = Gun(); + it("gun get path empty not", function(done){ + 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){ - var g = Gun(); + it("gun get path empty set", function(done){ + 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(); + }) }); });