Merge pull request #74 from amark/develop

Develop into Master fix Terje's bugs
This commit is contained in:
Mark Nadal 2015-07-11 21:22:38 -06:00
commit ad4aee81c8
3 changed files with 183 additions and 68 deletions

118
gun.js
View File

@ -367,7 +367,7 @@
function load(key){ function load(key){
if(Gun.fns.is(ctx.hook = gun.__.opt.hooks.get)){ if(Gun.fns.is(ctx.hook = gun.__.opt.hooks.get)){
ctx.hook(key, function(err, data){ // will be called multiple times. ctx.hook(key, function(err, data){ // will be called multiple times.
console.log("chain.get ", key, "from hook", err, data); console.log("chain.get ", key, "from hook", err, data, '\n');
if(err){ return cb.call(gun, err, null) } if(err){ return cb.call(gun, err, null) }
if(!data){ if(!data){
if(ctx.data){ return } if(ctx.data){ return }
@ -418,7 +418,7 @@
index({soul: opt.soul}); index({soul: opt.soul});
} else { // will be injected via a put } else { // will be injected via a put
(gun.__.flag.start[key] = gun._.at('soul')).once(function($){ (gun.__.flag.start[key] = gun._.at('soul')).once(function($){
console.log("chain.key"); console.log("chain.key", key, '\n');
(gun.__.key.s[key] = gun.__.key.s[key] || {})[$.soul] = gun.__.graph[$.soul]; (gun.__.key.s[key] = gun.__.key.s[key] || {})[$.soul] = gun.__.graph[$.soul];
delete gun.__.flag.start[key]; delete gun.__.flag.start[key];
}, -1); }, -1);
@ -477,44 +477,39 @@
Path ultimately should call .val each time, individually, for what it finds. Path ultimately should call .val each time, individually, for what it finds.
Things that wait and merge many things together should be an abstraction ontop of path. Things that wait and merge many things together should be an abstraction ontop of path.
*/ */
Chain.path = function(path, cb){ Chain.path = function(path, cb, opt){
var gun = this.chain(); var gun = this.chain();
cb = cb || function(){}; cb = cb || function(){};
opt = opt || {};
if((path !== null && !path) || !Gun.text.is(path = path.join? path.join('.') : path + '')){ return }
if(!gun.back._.at){ return cb.call(gun, {err: Gun.log("No context!")}), gun } if(!gun.back._.at){ return cb.call(gun, {err: Gun.log("No context!")}), gun }
// TODO: Hmmm once also? figure it out later.
gun.back._.at('soul').event(function($){ gun.back.on(function($, node){
var ctx = {path: (Gun.text.ify(path) || '').split('.')}; if(!(node = node || gun.__.graph[$.soul])){ return }
(function trace($){ // TODO: Check for field as well and merge? var chain = this || gun, src = opt.src || gun;
if(!ctx.path.length){ return } var ctx = {path: path.split('.')}, field = Gun.text.ify(ctx.path.shift());
var node = gun.__.graph[$.soul], field = Gun.text.ify(ctx.path.shift()), soul, val; var val = node[field], soul = Gun.is.soul(val);
if(ctx.path.length){ console.log("chain.path", field, node, '\n');
if(soul = Gun.is.soul(val = node[field])){ if(!field && !ctx.path.length){
gun.get(val, function(err, data){ cb.call(chain, null, node, field);
data = (data || {})[soul]; return opt.step? src._.at('soul').emit({soul: $.soul, field: null, from: opt.step.soul, at: opt.step.field, gun: chain, PATH: 'SOUL'})
if(err || !data || Gun.obj.empty(data, Gun._.meta)){ return cb.call(gun, err) } : src._.at('soul').emit({soul: $.soul, field: null, gun: chain, PATH: 'SOUL'});
trace({soul: soul}); }
}); if(!Gun.obj.has(node, field)){
} else { if(opt.end || (!ctx.path.length && gun.__.meta($.soul).end)){ // TODO: Make it so you can adjust how many terminations!
cb.call(gun, null); cb.call(chain, null, null, field);
} src._.at('soul').emit({soul: $.soul, field: field, gun: chain, PATH: 'SOUL'});
} else
if(!Gun.obj.has(node, field)){ // TODO: THIS MAY NOT BE CORRECT BEHAVIOR!!!!
cb.call(gun, null, null, field);
gun._.at('soul').emit({soul: $.soul, field: field, PATH: 'SOUL', WAS: 'ON'}); // if .put is after, makes sense. If anything else, makes sense to wait.
} else
if(soul = Gun.is.soul(val = node[field])){
gun.get(val, function(err, data){
data = (data || {})[soul];
cb.call(gun, err, data, field); // TODO: Should we attach field here, does map?
});
ctx.node = gun.__.graph[ctx.soul] = gun.__.graph[ctx.soul] || Gun.union.pseudo(soul);
gun._.at('soul').emit({soul: soul, field: null, from: $.soul, at: field, PATH: 'SOUL'});
} else {
cb.call(gun, null, val, field);
gun._.at('soul').emit({soul: $.soul, field: field, PATH: 'SOUL'});
} }
}($)); return;
}); }
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}});
}
cb.call(chain, null, val, field);
return src._.at('soul').emit({soul: $.soul, field: field, gun: chain, PATH: 'SOUL'});
}, {raw: true, once: true});
return gun; return gun;
} }
@ -536,11 +531,11 @@
if($.field){ if($.field){
if(ctx[$.soul + $.field]){ return } if(ctx[$.soul + $.field]){ return }
ctx[$.soul + $.field] = true; // TODO: unregister instead? ctx[$.soul + $.field] = true; // TODO: unregister instead?
return cb.call(gun, node[$.field], $.field || $.at); return cb.call($.gun || gun, node[$.field], $.field || $.at);
} }
if(ctx[$.soul] || ($.key && ctx[$.key]) || !gun.__.meta($.soul).end){ return } // TODO: Add opt to change number of terminations. if(ctx[$.soul] || ($.key && ctx[$.key]) || !gun.__.meta($.soul).end){ return } // TODO: Add opt to change number of terminations.
ctx[$.soul] = ctx[$.key] = true; // TODO: unregister instead? ctx[$.soul] = ctx[$.key] = true; // TODO: unregister instead?
cb.call(gun, Gun.obj.copy(node), $.field || $.at); cb.call($.gun || gun, Gun.obj.copy(node), $.field || $.at);
}, {raw: true}); }, {raw: true});
return gun; return gun;
@ -551,7 +546,6 @@
var gun = this, ctx = {}; var gun = this, ctx = {};
opt = Gun.obj.is(opt)? opt : {change: opt}; opt = Gun.obj.is(opt)? opt : {change: opt};
cb = cb || function(){}; cb = cb || function(){};
gun._.at('soul').event(function($){ // TODO: once per soul on graph. (?) gun._.at('soul').event(function($){ // TODO: once per soul on graph. (?)
if(ctx[$.soul]){ if(ctx[$.soul]){
if(opt.raw){ if(opt.raw){
@ -559,13 +553,15 @@
} }
} else { } else {
(ctx[$.soul] = function(delta, $$){ (ctx[$.soul] = function(delta, $$){
var $$ = $$ || $, node = gun.__.graph[$$.soul]; $$ = $$ || $; var node = gun.__.graph[$$.soul];
if(opt.raw){ return cb.call(gun, $$, delta, this) } if(delta && $.soul != Gun.is.soul.on(delta)){ return }
if(opt.raw){ return cb.call($$.gun || gun, $$, delta, this) }
if(!opt.end && Gun.obj.empty(delta, Gun._.meta)){ return } if(!opt.end && Gun.obj.empty(delta, Gun._.meta)){ return }
if($$.key){ node = Gun.union.pseudo($.key, gun.__.key.s[$.key]) || node } if($$.key){ node = Gun.union.pseudo($.key, gun.__.key.s[$.key]) || node }
cb.call(gun, Gun.obj.copy(opt.change? delta || node : node), $$.field || $$.at); if(opt.change){ node = delta || node }
cb.call($$.gun || gun, Gun.obj.copy($$.field? node[$$.field] : node), $$.field || $$.at);
})(gun.__.graph[$.soul], $); })(gun.__.graph[$.soul], $);
gun.__.on($.soul).event(ctx[$.soul]); if(!opt.once){ gun.__.on($.soul).event(ctx[$.soul]) }
} }
}); });
@ -595,8 +591,9 @@
if(gun.back.not){ gun.back.not(call) } if(gun.back.not){ gun.back.not(call) }
gun.back._.at('soul').event(function($){ // TODO: maybe once per soul? gun.back._.at('soul').event(function($){ // TODO: maybe once per soul?
var chain = $.gun || gun;
var ctx = {}, obj = val, $ = Gun.obj.copy($); var ctx = {}, obj = val, $ = Gun.obj.copy($);
console.log("chain.put", val); console.log("chain.put", val, '\n');
if(Gun.is.value(obj)){ if(Gun.is.value(obj)){
if($.from && $.at){ if($.from && $.at){
$.soul = $.from; $.soul = $.from;
@ -636,7 +633,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.join('.'), path); // TODO: clean this up. $.empty? path() : gun.back.path(at.path, path, {once: true, end: true}); // TODO: clean this up.
} }
} }
if(!at.node._[Gun._.HAM]){ if(!at.node._[Gun._.HAM]){
@ -645,7 +642,7 @@
if(!at.field){ return } if(!at.field){ return }
at.node._[Gun._.HAM][at.field] = drift; at.node._[Gun._.HAM][at.field] = drift;
})(function(err, ify){ })(function(err, ify){
console.log("chain.put PUT <----", ify.graph); console.log("chain.put PUT <----", ify.graph, '\n');
if(err || ify.err){ return cb.call(gun, err || ify.err) } if(err || ify.err){ return cb.call(gun, err || ify.err) }
if(err = Gun.union(gun, ify.graph).err){ return cb.call(gun, err) } if(err = Gun.union(gun, ify.graph).err){ return cb.call(gun, err) }
if($.from = Gun.is.soul(ify.root[$.field])){ $.soul = $.from; $.field = null } if($.from = Gun.is.soul(ify.root[$.field])){ $.soul = $.from; $.field = null }
@ -670,26 +667,23 @@
opt = (Gun.obj.is(opt)? opt : (opt? {node: true} : {})); opt = (Gun.obj.is(opt)? opt : (opt? {node: true} : {}));
cb = cb || function(){}; cb = cb || function(){};
gun.back.on(function(node){ // oo what if this gets TODO: BUG! retriggered? gun.back.on(function(node){
var soul = Gun.is.soul.on(node); var soul = Gun.is.soul.on(node);
console.log("chain.map", node, '\n');
Gun.obj.map(node, function(val, field){ // maybe filter against known fields. Gun.obj.map(node, function(val, field){ // maybe filter against known fields.
if(Gun._.meta == field){ return } if(Gun._.meta == field){ return }
var s = Gun.is.soul(val); var s = Gun.is.soul(val);
if(s){ if(s){
gun.get(val, function(err, data){ gun.get(val).on(function(d, f){
data = (data || {})[s]; // TODO: should map have support for `.not`? error? cb.call(this, d, f || field);
if(err || !data || Gun.obj.empty(data, Gun._.meta)){ return } gun._.at('soul').emit({soul: s, field: null, from: soul, at: field, MAP: 'SOUL', gun: this})
cb.call(this, Gun.obj.copy(data), field); }); //, {once: true});
});
gun.__.graph[s] = gun.__.graph[s] || Gun.union.pseudo(s);
gun._.at('soul').emit({soul: s, field: null, from: soul, at: field, MAP: 'SOUL'});
} else { } else {
if(opt.node){ return } // {node: true} maps over only sub nodes. if(opt.node){ return } // {node: true} maps over only sub nodes.
console.log("trigger next thing", field, val); cb.call(this, val, field);
cb.call(gun, val, field);
gun._.at('soul').emit({soul: soul, field: field, MAP: 'SOUL'}); gun._.at('soul').emit({soul: soul, field: field, MAP: 'SOUL'});
} }
}); }, this || gun);
}, true); }, true);
return gun; return gun;
@ -700,11 +694,11 @@
opt = opt || {}; opt = opt || {};
if(!gun.back){ gun = gun.put({}) } if(!gun.back){ gun = gun.put({}) }
gun = gun.not(function(next, key){ return key? this.put({}).key(key) : this.put({}) }); gun = gun.not(function(key){ return key? this.put({}).key(key) : this.put({}) });
if(!val && !Gun.is.value(val)){ return gun } if(!val && !Gun.is.value(val)){ return gun }
var obj = {}; var obj = {}, index = 'I' + drift + 'R' + Gun.text.random(5);
obj['I' + drift + 'R' + Gun.text.random(5)] = val; obj[index] = val;
return gun.put(obj, cb); return Gun.is.value(val)? gun.put(obj, cb) : gun.put(obj, cb).path(index);
} }
Chain.not = function(cb){ Chain.not = function(cb){
var gun = this, ctx = {}; var gun = this, ctx = {};
@ -716,7 +710,7 @@
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, kick, key), c = -1; }, chain = gun.chain(), next = cb.call(chain, 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 chain._.at('soul').emit({soul: Gun.roulette.call(chain), empty: true, key: key, N0T: 'SOUL', WAS: 'ON'}); // WAS ON
}); });

View File

@ -1,6 +1,6 @@
{ {
"name": "gun", "name": "gun",
"version": "0.2.0-alpha-2", "version": "0.2.0-alpha-3",
"description": "Graph engine", "description": "Graph engine",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -7,6 +7,7 @@ Gun.log.squelch = true;
describe('Gun', function(){ describe('Gun', function(){
var t = {}; var t = {};
describe('Utility', function(){ describe('Utility', function(){
it('verbose console.log debugging', function(done) { console.log("TURN THIS BACK ON the DEBUGGING TEST"); done(); return; it('verbose console.log debugging', function(done) { console.log("TURN THIS BACK ON the DEBUGGING TEST"); done(); return;
@ -829,7 +830,7 @@ describe('Gun', function(){
var gun = Gun(); var gun = Gun();
it('put', function(done){ it('put', function(done){
gun.put("hello", function(err){ gun.put("hello", function(err, ok){
expect(err).to.be.ok(); expect(err).to.be.ok();
done(); done();
}); });
@ -953,12 +954,13 @@ describe('Gun', function(){
}, 500); }, 500);
}); });
/* /* // not sure what this is suppose to do. Review later
it('get key no data put', function(done){ it('get key no data put', function(done){
gun.get('this/key/definitely/does/not/exist', function(err, data){ gun.get('this/key/definitely/does/not/exist', function(err, data){
expect(err).to.not.be.ok(); expect(err).to.not.be.ok();
expect(data).to.not.be.ok(); expect(data).to.not.be.ok();
}).put({testing: 'stuff'}, function(err, ok){ }).put({testing: 'stuff'}, function(err, ok){
console.log("what?", err, ok);
expect(err).to.not.be.ok(); expect(err).to.not.be.ok();
var node = gun.__.graph[done.soul]; var node = gun.__.graph[done.soul];
expect(node.hello).to.be('key'); expect(node.hello).to.be('key');
@ -1082,7 +1084,7 @@ describe('Gun', function(){
}); });
}); });
/* /* // Future feature!
it('put gun node', function(done){ it('put gun node', function(done){
var mark = gun.put({age: 23, name: "Mark Nadal"}); var mark = gun.put({age: 23, name: "Mark Nadal"});
var amber = gun.put({age: 23, name: "Amber Nadal"}); var amber = gun.put({age: 23, name: "Amber Nadal"});
@ -1548,7 +1550,7 @@ describe('Gun', function(){
}); });
it('double not', function(done){ // from the thought tutorial it('double not', function(done){ // from the thought tutorial
var gun = Gun().get('thoughts').not(function(n, key){ var gun = Gun().get('thoughts').not(function(key){
return this.put({}).key(key); return this.put({}).key(key);
}); });
@ -1584,7 +1586,7 @@ describe('Gun', function(){
}); });
gun.set(1).set(2).set(3).set(4); // if you set an object you'd have to do a `.back` gun.set(1).set(2).set(3).set(4); // if you set an object you'd have to do a `.back`
gun.map().val(function(val){ gun.map().val(function(val, field){
i += 1; i += 1;
expect(val).to.be(i); expect(val).to.be(i);
if(i % 4 === 0){ if(i % 4 === 0){
@ -1592,6 +1594,7 @@ describe('Gun', function(){
done.i = 0; done.i = 0;
Gun.obj.map(gun.__.graph, function(){ done.i++ }); Gun.obj.map(gun.__.graph, function(){ done.i++ });
expect(done.i).to.be(1); // make sure there isn't double. expect(done.i).to.be(1); // make sure there isn't double.
Gun.log.verbose = false;
done() done()
},10); },10);
} }
@ -1715,6 +1718,124 @@ describe('Gun', function(){
},10); },10);
},10); },10);
}); });
it("get map val -> map val", function(done){ // Terje's bug
var gun = Gun(); // we can test GUN locally.
var passengers = gun.get('passengers'); // this is now a list of passengers that we will map over.
var ctx = {n: 0, d: 0, l: 0};
passengers.map().val(function(passenger, id){
this.map().val(function(change, field){
//console.log("Passenger", passenger.name, "had", field, "change to:", change, '\n\n');
if('name' == field){ expect(change).to.be(passenger.name); ctx.n++ }
if('direction' == field){ expect(change).to.be(passenger.direction); ctx.d++ }
if('location' == field){
delete change._; ctx.l++;
if('Bob' == passenger.name){
expect(change).to.eql({'lat': '37.6159', 'lng': '-128.5'});
} else {
expect(change).to.eql({'lat': 'f37.6159', 'lng': 'f-128.5'});
}
}
if(ctx.n == 2 && ctx.d == 2 && ctx.l == 2){ done() }
});
});
var bob = passengers.set({
name: "Bob",
location: {'lat': '37.6159', 'lng': '-128.5'},
direction: '128.2'
});
var fred = passengers.set({
name: "Fred",
location: {'lat': 'f37.6159', 'lng': 'f-128.5'},
direction: 'f128.2'
});
});
it("get map map val", function(done){ // Terje's bug
var gun = Gun(); // we can test GUN locally.
var passengers = gun.get('passengers/map'); // this is now a list of passengers that we will map over.
var ctx = {n: 0, d: 0, l: 0};
passengers.map().map().val(function(val, field){
if('name' == field){ expect(val).to.be(!ctx.n? 'Bob' : 'Fred'); ctx.n++ }
if('direction' == field){ expect(val).to.be(!ctx.d? '128.2' : 'f128.2'); ctx.d++ }
if('location' == field){
delete val._;
if(!ctx.l){
expect(val).to.eql({'lat': '37.6159', 'lng': '-128.5'});
} else {
expect(val).to.eql({'lat': 'f37.6159', 'lng': 'f-128.5'});
}
ctx.l++;
}
if(ctx.n == 2 && ctx.d == 2 && ctx.l == 2){ done() }
});
var bob = passengers.set({
name: "Bob",
location: {'lat': '37.6159', 'lng': '-128.5'},
direction: '128.2'
});
setTimeout(function(){
var fred = passengers.set({
name: "Fred",
location: {'lat': 'f37.6159', 'lng': 'f-128.5'},
direction: 'f128.2'
});
},100);
});
it("get map path val", function(done){ // Terje's bug
var gun = Gun();
var ctx = {l: -1, d: 0};
var passengers = gun.get('passengers/path');
passengers.map().path('location.lng').val(function(val, field){
expect(field).to.be('lng');
if(ctx.l){
expect(val).to.be('-128.5');
} else {
expect(val).to.eql('f-128.5');
}
ctx.l++;
if(ctx.l){ done() }
});
var bob = passengers.set({
name: "Bob",
location: {'lat': '37.6159', 'lng': '-128.5'},
direction: '128.2'
});
setTimeout(function(){
var fred = passengers.set({
name: "Fred",
location: {'lat': 'f37.6159', 'lng': 'f-128.5'},
direction: 'f128.2'
});
},100);
});
it("put path deep val -> path val", function(done){ // Terje's bug
var gun = Gun();
gun.put({you: {have: {got: {to: {be: {kidding: "me!"}}}}}}).path('you.have.got.to.be').val(function(val, field){
expect(val.kidding).to.be('me!');
this.path('kidding').val(function(val){
expect(val).to.be('me!');
done();
});
});
});
it("get set path put, map path val -> path val", function(done){ // Terje's bug
var gun = Gun();
var ctx = {l: -1, d: 0};
var passengers = gun.get('passengers/set/path');
passengers.set({name: 'Bob'}).path('direction').put({lol: {just: 'kidding', dude: '!'}}, function(err, ok){});
passengers.map().path('direction.lol').val(function(val){
this.path('just').val(function(val){
expect(val).to.be('kidding');
}).back.path('dude').val(function(val){
expect(val).to.be('!');
done();
});
})
});
}); });
describe('Streams', function(){ describe('Streams', function(){