diff --git a/gun.js b/gun.js index e56cac99..1bde16fe 100644 --- a/gun.js +++ b/gun.js @@ -722,7 +722,6 @@ function unlink(msg, cat){ // ugh, so much code for seemingly edge case behavior. var put = msg.put||'', change = put['=']||put[':'], root = cat.root, link, tmp; - //if(!cat.has){ return } // MARK COME BACK HERE !!!!!!!!!!!!!!!! undef not need to trigger undef! No, already? if(u === change){ // 1st edge case: If we have a brand new database, no data will be found. // TODO: BUG! because emptying cache could be async from below, make sure we are not emptying a newer cache. So maybe pass an Async ID to check against? // TODO: BUG! What if this is a map? // Warning! Clearing things out needs to be robust against sync/async ops, or else you'll see `map val get put` test catastrophically fail because map attempts to link when parent graph is streamed before child value gets set. Need to differentiate between lack acks and force clearing. @@ -730,28 +729,27 @@ //if(!cat.has){ return } tmp = (msg.$$||msg.$||'')._||''; if(msg['@'] && (u !== tmp.put || u !== cat.put)){ return } // a "not found" from other peers should not clear out data if we have already found it. - if(cat.has && u === cat.put && !(root.pass||'')[cat.id]){ return } // if we are already unlinked, do not call again, unless edge case. - //console.log('unlink:', cat.id, cat.has, msg, cat.link, '?', cat.put, change, '!!!', (root.pass||'')[cat.id]); + //if(cat.has && u === cat.put && !(root.pass||'')[cat.id]){ return } // if we are already unlinked, do not call again, unless edge case. // TODO: BUG! This line should be deleted for "unlink deeply nested". + if(link = cat.link){ + delete (root.$.get(link)._.echo||'')[cat.id]; + } if(cat.has){ // TODO: Empty out links, maps, echos, acks/asks, etc.? - if(tmp = cat.link){ - delete (root.$.get(tmp)._.echo||'')[cat.id]; - } cat.link = null; } cat.put = u; // empty out the cache if, for example, alice's car's color no longer exists (relative to alice) if alice no longer has a car. // TODO: BUG! For maps, proxy this so the individual sub is triggered, not all subs. - //console.log("unlink!", cat.id, msg, cat); setTimeout.each(Object.keys(cat.next||''), function(get, sat){ // empty out all sub chains. // TODO: .keys( is slow // BUG? ?Some re-in logic may depend on this being sync? // TODO: BUG? This will trigger deeper put first, does put logic depend on nested order? // TODO: BUG! For map, this needs to be the isolated child, not all of them. if(!(sat = cat.next[get])){ return } - if(cat.has && u === sat.put && !(root.pass||'')[sat.id]){ return } // if we are already unlinked, do not call again, unless edge case. + //if(cat.has && u === sat.put && !(root.pass||'')[sat.id]){ return } // if we are already unlinked, do not call again, unless edge case. // TODO: BUG! This line should be deleted for "unlink deeply nested". + if(link){ delete (root.$.get(link).get(get)._.echo||'')[sat.id] } sat.on('in', {get: get, put: u, $: sat.$}); // TODO: BUG? Add recursive seen check? },0,99); return; } if(cat.soul){ return } // a soul cannot unlink itself. if(msg.$$){ return } // a linked chain does not do the unlinking, the sub chain does. // TODO: BUG? Will this cancel maps? - link = valid(change); // need to unlink anytime we are not the same link, though only do this once per unlink. - tmp = msg.$._; + link = valid(change); // need to unlink anytime we are not the same link, though only do this once per unlink (and not on init). + tmp = msg.$._||''; if(link === tmp.link || (cat.has && !tmp.link)){ if((root.pass||'')[cat.id] && 'string' !== typeof link){ @@ -759,8 +757,8 @@ return; } } - unlink({get: cat.get, put: u, $: cat.$}, cat); // unlink our sub chains. - delete (msg.$._.echo||'')[cat.id]; + delete (tmp.echo||'')[cat.id]; + unlink({get: cat.get, put: u, $: msg.$}, cat); // unlink our sub chains. }; Gun.on.unlink = unlink; function ack(msg, ev){ diff --git a/test/common.js b/test/common.js index e42eed62..abbe41a8 100644 --- a/test/common.js +++ b/test/common.js @@ -1511,7 +1511,7 @@ describe('Gun', function(){ }, 1000); }); - it('uncached synchronous map on get node mutate node uncached', function(done){ + it.only('uncached synchronous map on get node mutate node uncached', function(done){ Gun.statedisk({ alice: {_:{'#':'umaliceo3'}, age: 26, @@ -1528,15 +1528,15 @@ describe('Gun', function(){ gun.get('u/m/p/n/mutate/n/u').map().get('pet').on(function(v,f){ check[v.name] = f; count[v.name] = (count[v.name] || 0) + 1; - //console.log("*****************", f,v); + console.log("*****************", f,v, check); if(check.Fluffy && check.Frisky && check.Fuzzball){ clearTimeout(done.to); done.to = setTimeout(function(){ expect(done.last).to.be.ok(); expect(check['Fluffs']).to.not.be.ok(); - //expect(count.Fluffy).to.be(1); - //expect(count.Frisky).to.be(1); - //expect(count.Fuzzball).to.be(1); + expect(count.Fluffy).to.be(1); + expect(count.Frisky).to.be(1); + expect(count.Fuzzball).to.be(1); done(); },200); } @@ -1560,6 +1560,51 @@ describe('Gun', function(){ }, 1000); }); + it("unlink deeply nested", function(done){ + Gun.statedisk({ + a: {_:{'#':'audn'}, + age: 26, + name: "Alice", + b: {_:{'#':'budn'}, c: {_:{'#':'cudn'}, id: 'first', level: 3}, level: 2} + } + }, 'udn', function() { + var check = {}, count = {}; + gun.get('udn').get('a').get('b').get('c').on(function(data){ + //console.log("udn.a.b.c:", data); + check[data.id] = 1; + count[data.id] = (count[data.id] || 0) + 1; + expect(data.foo).to.not.be.ok(); + //console.log("*****************", f,v, check); + if(check.first && check.other){ + clearTimeout(done.to); + done.to = setTimeout(function(){ + expect(done.last).to.be.ok(); + expect(check.firsta).to.not.be.ok(); + expect(count.first).to.be(1); + expect(count.other).to.be(1); + done(); + },200); + } + }); + setTimeout(function(){ + Gun.statedisk({ + name: 'Alice2', age: 34, + b: {_:{'#':'2budn'}, c: {_:{'#':'2cudn'}, id: 'other', level: 3}, level: 2} + }, '2audn', function() { + //console.only.i=1;console.log('============================='); + gun.get('udn').put({ + a: {'#':'2audn'} + }); + setTimeout(function(){ + //console.log("- - - - - - - - - - - -"); + gun.get('cudn').put({id: 'firsta', foo: 'bar'}); + done.last = 1; + }, 50); + }); + },50); + }); + }); + it("get before put in memory", function(done){ var gun = Gun(); var check = {};