This commit is contained in:
Mark Nadal 2015-02-08 20:48:47 -07:00
parent b96ecbdadb
commit 3e9bb356a4
7 changed files with 194 additions and 112 deletions

View File

@ -10,8 +10,10 @@
// by Forrest Tait! Edited by Mark Nadal. // by Forrest Tait! Edited by Mark Nadal.
function ready(){ function ready(){
var $ = document.querySelector.bind(document); var $ = document.querySelector.bind(document);
Gun.log.verbose = true;
var gun = Gun(location.origin + '/gun').load('example/todo/data').set({}); var gun = Gun(location.origin + '/gun').load('example/todo/data').set({});
gun.on(function renderToDo(val){ gun.on(function renderToDo(val){
console.log("TODO ON:", val);
var todoHTML = ''; var todoHTML = '';
for(key in val) { for(key in val) {
if(!val[key] || key == '_') continue; if(!val[key] || key == '_') continue;

187
gun.js
View File

@ -237,7 +237,7 @@
gun.shot('done'); gun.shot('done');
gun.shot.done(function(){ gun.shot.done(function(){
opt = opt || {}; opt = opt || {};
cb = cb || gun.__.opt.cb; cb = cb || function(){};
cb.soul = (key||{})[Gun._.soul]; // is this a key or a soul? cb.soul = (key||{})[Gun._.soul]; // is this a key or a soul?
if(cb.soul){ // if a soul... if(cb.soul){ // if a soul...
cb.node = gun.__.graph[cb.soul]; // then attempt to grab it directly from cache in the graph cb.node = gun.__.graph[cb.soul]; // then attempt to grab it directly from cache in the graph
@ -252,13 +252,19 @@
gun.shot('then').fire(); gun.shot('then').fire();
return; return;
} }
cb.fn = function(){} cb.fn = function(){}; cb.count = 0;
// missing: hear shots! I now hook this up in other places, but we could get async/latency issues? // missing: hear shots! I now hook this up in other places, but we could get async/latency issues?
// We need to subscribe early? Or the transport layer handle this for us? // We need to subscribe early? Or the transport layer handle this for us?
if(Gun.fns.is(gun.__.opt.hooks.load)){ if(Gun.fns.is(gun.__.opt.hooks.load)){
gun.__.opt.hooks.load(key, function(err, data){ gun.__.opt.hooks.load(key, function(err, data){
console.log("count?", cb.count += 1);
if(err){ return cb.call(gun, err), (gun._.err||cb.fn).call(gun, err) } if(err){ return cb.call(gun, err), (gun._.err||cb.fn).call(gun, err) }
if(!data){ return cb.call(gun, err, data), gun.shot('then').fire() } // if no data, emit without any contexxt change if(!data){ // if no data, emit without any context change
cb.call(gun, err, data)
//if(!cb.once){
gun.shot('then').fire()
//}
}
var context = gun.union(data); // safely transform the data into the current context var context = gun.union(data); // safely transform the data into the current context
if(context.err){ return cb.call(gun, context.err), (gun._.err||cb.fn).call(gun, context.err) } // but validate it in case of errors if(context.err){ return cb.call(gun, context.err), (gun._.err||cb.fn).call(gun, context.err) } // but validate it in case of errors
gun._.node = gun.__.graph[data._[Gun._.soul]]; // immediately use the state in cache, no waiting for union to be done. gun._.node = gun.__.graph[data._[Gun._.soul]]; // immediately use the state in cache, no waiting for union to be done.
@ -267,10 +273,15 @@
gun.__.keys[key] = gun._.node; // and cache a pointer to the node gun.__.keys[key] = gun._.node; // and cache a pointer to the node
} }
cb.call(gun, null, Gun.obj.copy(gun._.node)); // frozen copy cb.call(gun, null, Gun.obj.copy(gun._.node)); // frozen copy
gun.shot('then').fire(); //if(!cb.once){
gun.shot('then').fire()
//}
cb.once = true;
}, opt); }, opt);
} else { } else {
cb.call(gun, {err: Gun.log("Warning! You have no persistence layer to load from!")}); root.console.log("Warning! You have no persistence layer to load from!");
cb.call(gun);
gun.shot('then').fire();
} }
}); });
gun.shot('done').fire(); // because we are loading, we always fire! gun.shot('done').fire(); // because we are loading, we always fire!
@ -279,7 +290,7 @@
Chain.key = function(key, cb){ Chain.key = function(key, cb){
var gun = this; var gun = this;
gun.shot.then(function(){ gun.shot.then(function(){
cb = cb || gun.__.opt.cb; cb = cb || function(){};
if(Gun.obj.is(key)){ // if key is an object then we get the soul directly from it if(Gun.obj.is(key)){ // if key is an object then we get the soul directly from it
Gun.obj.map(key, function(soul, field){ return cb.key = field, cb.soul = soul }); Gun.obj.map(key, function(soul, field){ return cb.key = field, cb.soul = soul });
} else { // or else } else { // or else
@ -295,10 +306,12 @@
if(Gun.fns.is(gun.__.opt.hooks.key)){ if(Gun.fns.is(gun.__.opt.hooks.key)){
gun.__.opt.hooks.key(cb.key, cb.soul, function(err, data){ gun.__.opt.hooks.key(cb.key, cb.soul, function(err, data){
if(err){ return cb.call(gun, err) } if(err){ return cb.call(gun, err) }
return cb.call(gun, null); return cb.call(gun, data);
}); });
} else { } else {
cb.call(gun, {err: Gun.log("Warning! You have no key hook!")}); root.console.log("Warning! You have no key hook!");
cb.call(gun);
gun.shot('then').fire();
} }
}); });
return gun; return gun;
@ -318,17 +331,20 @@
Path ultimately should call .get each time, individually, for what it finds. Path ultimately should call .get 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){ // Follow the path into the field. Chain.path = function(path, cb){ // Follow the path into the field.
var gun = this.chain(); // create a new context, changing the focal point. var gun = this.chain(); // create a new context, changing the focal point.
path = (path || '').split('.'); path = (path || '').split('.');
gun.back.shot.then(function(){ // let the previous promise resolve. gun.back.shot.then(function(){ // let the previous promise resolve.
(function trace(){ // create a recursive function, and immediately call it. (function trace(){ // create a recursive function, and immediately call it.
cb = cb || function(){}; // fail safe our function.
gun._.field = Gun.text.ify(path.shift()); // where are we at? Figure it out. gun._.field = Gun.text.ify(path.shift()); // where are we at? Figure it out.
if(gun._.node && path.length && Gun.is.soul(gun._.node[gun._.field])){ // if we need to recurse more if(gun._.node && path.length && Gun.is.soul(gun._.node[gun._.field])){ // if we need to recurse more
return gun.load(val, function(){ // and the recursion happens to be on a relation, then load it. return gun.load(val, function(err){ // and the recursion happens to be on a relation, then load it.
if(err){ return cb.call(gun, err) }
trace(gun._ = this._); // follow the context down the chain. trace(gun._ = this._); // follow the context down the chain.
}); });
} }
cb.call(gun, null, Gun.obj.copy(gun._.node), gun._.field); // frozen copy
gun.shot('then').fire(); // and be done, fire our gun with the context. gun.shot('then').fire(); // and be done, fire our gun with the context.
}(gun._.node = gun.back._.node)); // immediately call trace, setting the new context with the previous node. }(gun._.node = gun.back._.node)); // immediately call trace, setting the new context with the previous node.
}); });
@ -337,13 +353,13 @@
Chain.get = function(cb){ Chain.get = function(cb){
var gun = this; // keep using the existing context. var gun = this; // keep using the existing context.
gun.shot.then(function get(){ // let the previous promise resolve. gun.shot.then(function get(){ // let the previous promise resolve.
cb = cb || gun.__.opt.cb; // fail safe our function. cb = cb || function(){}; // fail safe our function.
if(!gun._.node){ return } // if no node, then abandon and let blank handle it. if(!gun._.node){ return } // if no node, then abandon and let blank handle it.
var val = gun._.node[gun._.field]; // else attempt to get the value at the field, if we have a field. var field = gun._.field, val = gun._.node[field]; // else attempt to get the value at the field, if we have a field.
if(gun._.field && Gun.is.soul(val)){ // if we have a field, then check to see if it is a relation if(field && Gun.is.soul(val)){ // if we have a field, then check to see if it is a relation
return gun.load(val, function(){ // and load it. return gun.load(val, function(err, val){ // and load it.
this._.field = gun._.field; // preserve the field information if(!this._.node){ return }
get(gun._ = this._); // and pass forward the context. cb.call(this, val, field); // already frozen copy
}); });
} }
cb.call(gun, gun._.field? Gun.is.value.as(val) : Gun.obj.copy(gun._.node), gun._.field); // frozen copy cb.call(gun, gun._.field? Gun.is.value.as(val) : Gun.obj.copy(gun._.node), gun._.field); // frozen copy
@ -352,8 +368,9 @@
} }
Chain.on = function(cb){ // get and then subscribe to subsequent changes. Chain.on = function(cb){ // get and then subscribe to subsequent changes.
var gun = this; // keep using the existing context. var gun = this; // keep using the existing context.
gun.get(function(val, field){ // wrap get! gun.get(function(val, field){ // wrap get! TODO: Shoot, can't wrap get or else non-existence fails.
cb = cb || gun.__.opt.cb; // fail safe our function. console.log("GET ON with this", this._);
cb = cb || function(){}; // fail safe our function.
cb.call(gun, val, field); // call the listener for the initial load cb.call(gun, val, field); // call the listener for the initial load
gun.__.on(Gun.is.soul.on(gun._.node)).event(function(delta){ // then subscribe to subsequent changes. gun.__.on(Gun.is.soul.on(gun._.node)).event(function(delta){ // then subscribe to subsequent changes.
if(!delta || !gun._.node){ return } if(!delta || !gun._.node){ return }
@ -381,42 +398,37 @@
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.set = function(val, cb, opt){ // TODO: need to turn deserializer into a trampolining function so stackoverflow doesn't happen. Chain.set = function(val, cb, opt){ // TODO: need to turn deserializer into a trampolining function so stackoverflow doesn't happen.
var gun = this, set = {}; var on = this, gun = on.chain();
gun.shot.then(function(){ // HOLY SMOKES TODO: BUG! Sub-objects need to merge with existing objects if they exist, aka guarantee same soul. on.shot.then(function(){
gun._ = on._;
opt = opt || {}; opt = opt || {};
cb = cb || gun.__.opt.cb; cb = cb || function(){};
/*
if(!gun._.node){ if(!gun._.node){
if(Gun.is.value(val) || !Gun.is.obj(val)){ if(Gun.is.value(val) || !Gun.obj.is(val)){
return cb.call(gun, {err: Gun.log("No field exists to set the " + (typeof val) + " on.")}); return cb.call(gun, {err: Gun.log("No field exists to set the " + (typeof val) + " on.")});
} }
} } else
if(gun._.field){
if(Gun.is.soul(gun._.node[gun._.field])){
// load it in to merge.
} else {
}
} else {
}
return;
*/
if(gun._.field){ // if a field exists, it should always be a string if(gun._.field){ // if a field exists, it should always be a string
var partial = {}; // in case we are doing a set on a field, not on a node var partial = {}; // in case we are doing a set on a field, not on a node
partial[gun._.field] = val; // we create a blank node with the field/value to be set partial[gun._.field] = val; // we create a blank node with the field/value to be set
val = partial; val = partial;
} else
if(!Gun.obj.is(val)){
return cb.call(gun, {err: Gun.log("No field exists to set the " + (typeof val) + " on.")});
} }
var n; if(gun._.field && Gun.is.soul(gun._.node[gun._.field])){ n = {}; n[Gun._.meta] = gun._.node[gun._.field]; } Gun.ify(val, function(raw, context, sub, soul){
else { n = gun._.node || val; } if(val === raw){ return soul(Gun.is.soul.on(gun._.node)) }
if(on._.node && sub && sub.path){
return on.path(sub.path, function(err, node, field){
if(err){ cb.err = err + " (while doing a set)" } // let .done handle calling this, it may be slower but is more consistent.
if(this._.node && (field = Gun.is.soul(this._.node[this._.field]))){
return soul(field);
} soul(); // else call it anyways
});
} soul(); // else call it anyways
}).done(function(err, set){
console.log("SET done");
// TODO: should be able to handle val being a relation or a gun context or a gun promise. // TODO: should be able to handle val being a relation or a gun context or a gun promise.
// TODO: BUG: IF we are setting an object, doing a partial merge, and they are reusing a frozen copy, we need to do a DIFF to update the HAM! Or else we'll get "old" HAM. // TODO: BUG: IF we are setting an object, doing a partial merge, and they are reusing a frozen copy, we need to do a DIFF to update the HAM! Or else we'll get "old" HAM.
val._ = Gun.ify.soul.call(gun, {}, n); // set their souls to be the same that way they will merge correctly for us during the union!
set = Gun.ify.call(gun, val, set);
cb.root = set.root; cb.root = set.root;
set.err = set.err || cb.err;
if(set.err || !cb.root){ return cb.call(gun, set.err || {err: Gun.log("No root object!")}) } if(set.err || !cb.root){ return cb.call(gun, set.err || {err: Gun.log("No root object!")}) }
set = Gun.ify.state(set.nodes, Gun.time.is()); // set time state on nodes? set = Gun.ify.state(set.nodes, Gun.time.is()); // set time state on nodes?
if(set.err){ return cb.call(gun, set.err) } if(set.err){ return cb.call(gun, set.err) }
@ -431,14 +443,18 @@
if(Gun.fns.is(gun.__.opt.hooks.set)){ if(Gun.fns.is(gun.__.opt.hooks.set)){
gun.__.opt.hooks.set(set.nodes, function(err, data){ // now iterate through those nodes to a persistence layer and get a callback once all are saved gun.__.opt.hooks.set(set.nodes, function(err, data){ // now iterate through those nodes to a persistence layer and get a callback once all are saved
if(err){ return cb.call(gun, err) } if(err){ return cb.call(gun, err) }
return cb.call(gun, null); cb.call(gun, data);
gun.shot('then').fire();
}); });
} else { } else {
cb.call(gun, {err: Gun.log("Warning! You have no persistence layer to save to!")}) root.console.log("Warning! You have no persistence layer to save to!");
cb.call(gun);
gun.shot('then').fire();
} }
}); });
if(!gun.back){ });
gun.shot('then').fire(); if(!on.back){
on.shot('then').fire();
} }
return gun; return gun;
} }
@ -446,7 +462,7 @@
var gun = this; var gun = this;
gun.get(function(val){ gun.get(function(val){
opt = opt || {}; opt = opt || {};
cb = cb || gun.__.opt.cb; cb = cb || function(){};
Gun.obj.map(val, function(val, field){ // by default it only maps over nodes Gun.obj.map(val, function(val, field){ // by default it only maps over nodes
if(Gun._.meta == field){ return } if(Gun._.meta == field){ return }
if(Gun.is.soul(val)){ if(Gun.is.soul(val)){
@ -466,7 +482,7 @@
// Meaning it is more low level, such that even set uses union internally. // Meaning it is more low level, such that even set uses union internally.
Chain.union = function(prime, cb){ Chain.union = function(prime, cb){
var tmp = {}, gun = this, context = Gun.shot(); var tmp = {}, gun = this, context = Gun.shot();
cb = cb || gun.__.opt.cb cb = cb || function(){};
context.nodes = {}; context.nodes = {};
if(!prime){ if(!prime){
context.err = {err: Gun.log("No data to merge!")}; context.err = {err: Gun.log("No data to merge!")};
@ -765,14 +781,14 @@
} }
}({})); }({}));
;(function(Serializer){ ;(function(Serializer){
Gun.ify = function(data, bind){ // TODO: BUG: Modify lists to include HAM state Gun.ify = function(data, cb){ // TODO: BUG: Modify lists to include HAM state
if(Gun.obj.map(bind, function(){ return true })){ return {err: Gun.log("Bind must be an empty object.")} }
var gun = Gun.is(this)? this : {} var gun = Gun.is(this)? this : {}
, context = { , nothing, context = Gun.shot();
nodes: {} context.nodes = {};
,seen: [] context.seen = [];
,_seen: [] context.seen = [];
}, nothing; context('done');
cb = cb || function(){};
function ify(data, context, sub){ function ify(data, context, sub){
sub = sub || {}; sub = sub || {};
sub.path = sub.path || ''; sub.path = sub.path || '';
@ -782,10 +798,9 @@
return data; return data;
} else } else
if(Gun.obj.is(data)){ if(Gun.obj.is(data)){
var value = bind || {}, symbol = {}, seen var value = {}, meta = {}, seen
, err = {err: "Metadata does not support external or circular references at " + sub.path, meta: true}; , err = {err: "Metadata does not support external or circular references at " + sub.path, meta: true};
context.root = context.root || value; context.root = context.root || value;
bind = null;
if(seen = ify.seen(context._seen, data)){ if(seen = ify.seen(context._seen, data)){
//console.log("seen in _", sub._, sub.path, data); //console.log("seen in _", sub._, sub.path, data);
Gun.log(context.err = err); Gun.log(context.err = err);
@ -797,20 +812,26 @@
Gun.log(context.err = err); Gun.log(context.err = err);
return; return;
} }
symbol = Gun.ify.soul.call(gun, symbol, seen); meta = Gun.ify.soul.call(gun, meta, seen);
return symbol; return meta;
} else { } else {
//console.log("seen nowhere", sub._, sub.path, data); //console.log("seen nowhere", sub._, sub.path, data);
if(sub._){ if(sub._){
context.seen.push({data: data, node: value}); context.seen.push({data: data, node: value});
} else { } else {
value._ = Gun.ify.soul.call(gun, {}, data); value._ = {};
cb(data, context, sub, context.many.add(function(soul){
//console.log("What soul did we find?", soul || "random");
meta[Gun._.soul] = value._[Gun._.soul] = soul = Gun.is.soul.on(data) || soul || Gun.roulette();
context.nodes[soul] = value;
this.done();
}));
context.seen.push({data: data, node: value}); context.seen.push({data: data, node: value});
context.nodes[value._[Gun._.soul]] = value;
} }
} }
Gun.obj.map(data, function(val, field){ Gun.obj.map(data, function(val, field){
var subs = {path: sub.path + field + '.', _: sub._ || (field == Gun._.meta)? true : false }; var subs = {path: sub.path? sub.path + '.' + field : field,
_: sub._ || (field == Gun._.meta)? true : false };
val = ify(val, context, subs); val = ify(val, context, subs);
//console.log('>>>>', sub.path + field, 'is', val); //console.log('>>>>', sub.path + field, 'is', val);
if(context.err){ return true } if(context.err){ return true }
@ -819,9 +840,8 @@
value[field] = val; value[field] = val;
}); });
if(sub._){ return value } if(sub._){ return value }
if(!value._ || !value._[Gun._.soul]){ return } if(!value._){ return }
symbol[Gun._.soul] = value._[Gun._.soul]; return meta;
return symbol;
} else } else
if(Gun.list.is(data)){ if(Gun.list.is(data)){
var unique = {}, edges var unique = {}, edges
@ -855,7 +875,11 @@
if(check.data === data){ return check.node } if(check.data === data){ return check.node }
}); });
} }
context.many = Gun.fns.sum(function(err){ context('done').fire(context.err, context) });
context.many.add(function(){
ify(data, context); ify(data, context);
this.done();
})();
return context; return context;
} }
Gun.ify.state = function(nodes, now){ Gun.ify.state = function(nodes, now){
@ -909,7 +933,7 @@
tab.prenode = tab.prenode || opt.prenode || '_/nodes/'; tab.prenode = tab.prenode || opt.prenode || '_/nodes/';
tab.load = tab.load || function(key, cb, opt){ tab.load = tab.load || function(key, cb, opt){
if(!key){ return } if(!key){ return }
cb = cb || gun.__.opt.cb; cb = cb || function(){};
opt = opt || {}; opt = opt || {};
opt.url = opt.url || {}; opt.url = opt.url || {};
opt.headers = tab.headers; opt.headers = tab.headers;
@ -923,20 +947,20 @@
var node, lkey = key[Gun._.soul]? tab.prefix + tab.prenode + key[Gun._.soul] var node, lkey = key[Gun._.soul]? tab.prefix + tab.prenode + key[Gun._.soul]
: tab.prefix + tab.prekey + key : tab.prefix + tab.prekey + key
if((node = store.get(lkey)) && node[Gun._.soul]){ return local(node, cb) } if((node = store.get(lkey)) && node[Gun._.soul]){ return local(node, cb) }
if(node){ setTimeout(function(){cb(null, node)},0) } if(cb.node = node){ setTimeout(function(){cb(null, node)},0) }
}(key, cb)); }(key, cb));
Gun.obj.map(gun.__.opt.peers, function(peer, url){ Gun.obj.map(gun.__.opt.peers, function(peer, url){
request(url, null, function(err, reply){ request(url, null, function(err, reply){
Gun.log('via', url, key, reply.body); Gun.log('via', url, key, reply.body);
if(err || !reply){ return } // handle reconnect? if(err || !reply || (err = reply.body && reply.body.err)){
if(reply.body && reply.body.err){ cb({err: Gun.log(err || "Error: Load failed through " + url) });
cb(reply.body.err);
} else { } else {
if(!key[Gun._.soul] && Gun.is.soul(reply.body)){ if(!key[Gun._.soul] && Gun.is.soul(reply.body)){
var meta = {}; var meta = {};
meta[Gun._.soul] = Gun.is.soul(reply.body); meta[Gun._.soul] = Gun.is.soul(reply.body);
store.set(tab.prefix + tab.prekey + key, meta); store.set(tab.prefix + tab.prekey + key, meta);
} }
if(cb.node){ console.log("oh boy"); return gun.__.on(Gun.is.soul(reply.body)).emit(reply.body) }
cb(null, reply.body); cb(null, reply.body);
} }
}, opt); }, opt);
@ -950,18 +974,18 @@
store.set(tab.prefix + tab.prekey + key, meta); store.set(tab.prefix + tab.prekey + key, meta);
Gun.obj.map(gun.__.opt.peers, function(peer, url){ Gun.obj.map(gun.__.opt.peers, function(peer, url){
request(url, meta, function(err, reply){ request(url, meta, function(err, reply){
if(err || !reply){ if(err || !reply || (err = reply.body && reply.body.err)){
// tab.key(key, soul, cb); // naive implementation of retry TODO: BUG: need backoff and anti-infinite-loop! // tab.key(key, soul, cb); // naive implementation of retry TODO: BUG: need backoff and anti-infinite-loop!
cb({err: Gun.log(err || "Error: Key failed to be made on " + url) }); cb({err: Gun.log(err || "Error: Key failed to be made on " + url) });
} else { } else {
cb(); cb(null, reply.body);
} }
}, {url: {pathname: '/' + key }, headers: tab.headers}); }, {url: {pathname: '/' + key }, headers: tab.headers});
cb.peers = true; cb.peers = true;
}); tab.peers(cb); }); tab.peers(cb);
} }
tab.set = tab.set || function(nodes, cb){ tab.set = tab.set || function(nodes, cb){
cb = cb || gun.__.opt.cb; cb = cb || function(){};
// TODO: batch and throttle later. // TODO: batch and throttle later.
// tab.store.set(cb.id = 'send/' + Gun.text.random(), nodes); // TODO: store SENDS until SENT. // tab.store.set(cb.id = 'send/' + Gun.text.random(), nodes); // TODO: store SENDS until SENT.
Gun.obj.map(nodes, function(node, soul){ Gun.obj.map(nodes, function(node, soul){
@ -969,7 +993,13 @@
store.set(tab.prefix + tab.prenode + soul, gun.__.graph[soul]); store.set(tab.prefix + tab.prenode + soul, gun.__.graph[soul]);
}); });
Gun.obj.map(gun.__.opt.peers, function(peer, url){ Gun.obj.map(gun.__.opt.peers, function(peer, url){
request(url, nodes, function respond(err, reply, id){ request(url, nodes, function(err, reply){
if(err || !reply || (err = reply.body && reply.body.err)){
return cb({err: Gun.log(err || "Error: Set failed on " + url) });
} else {
console.log("has set?", err, reply.body);
cb(null, reply.body);
}
}, {headers: tab.headers}); }, {headers: tab.headers});
cb.peers = true; cb.peers = true;
}); tab.peers(cb); }); tab.peers(cb);
@ -979,7 +1009,7 @@
} }
tab.peers = function(cb){ tab.peers = function(cb){
if(cb && !cb.peers){ // there are no peers! this is a local only instance if(cb && !cb.peers){ // there are no peers! this is a local only instance
setTimeout(function(){cb({err: Gun.log("Warning! You have no peers to connect to!")})},1); setTimeout(function(){console.log("Warning! You have no peers to connect to!");cb()},1);
} }
} }
tab.set.defer = {}; tab.set.defer = {};
@ -1012,7 +1042,7 @@
} }
r.createServer = function(fn){ (r.createServer = fn).on = true } r.createServer = function(fn){ (r.createServer = fn).on = true }
r.transport = function(opt, cb){ r.transport = function(opt, cb){
console.log("TRANSPORT:", opt); //Gun.log("TRANSPORT:", opt);
if(r.ws(opt, cb)){ return } if(r.ws(opt, cb)){ return }
r.jsonp(opt, cb); r.jsonp(opt, cb);
} }
@ -1030,7 +1060,6 @@
delete r.ws.cbs[req.headers['ws-rid']]; delete r.ws.cbs[req.headers['ws-rid']];
cb(err,res); cb(err,res);
} }
console.log("WS send:", req);
ws.send(JSON.stringify(req)); ws.send(JSON.stringify(req));
return true; return true;
} }
@ -1053,8 +1082,8 @@
}catch(e){ return } }catch(e){ return }
if(!res){ return } if(!res){ return }
res.headers = res.headers || {}; res.headers = res.headers || {};
if(res.headers['ws-rid']){ (r.ws.cbs[res.headers['ws-rid']]||function(){})(null, res) } if(res.headers['ws-rid']){ return (r.ws.cbs[res.headers['ws-rid']]||function(){})(null, res) }
//Gun.log("We have a pushed message!", res); Gun.log("We have a pushed message!", res);
if(res.body){ r.createServer(res, function(){}) } // emit extra events. if(res.body){ r.createServer(res, function(){}) } // emit extra events.
}; };
ws.onerror = function(e){ Gun.log(e); }; ws.onerror = function(e){ Gun.log(e); };

View File

@ -24,7 +24,8 @@
s3.get(key, function(err, data, text, meta){ s3.get(key, function(err, data, text, meta){
Gun.log('via s3', key, err); Gun.log('via s3', key, err);
if(meta && meta[Gun._.soul]){ if(meta && meta[Gun._.soul]){
return s3.load(meta, cb); return s3.load(meta, cb); // SUPER SUPER IMPORTANT TODO!!!! Make this load via GUN in case soul is already cached!
// HUGE HUGE HUGE performance gains could come from the above line being updated! (not that this module is performant).
} }
if(err && err.statusCode == 404){ if(err && err.statusCode == 404){
err = null; // we want a difference between 'unfound' (data is null) and 'error' (auth is wrong). err = null; // we want a difference between 'unfound' (data is null) and 'error' (auth is wrong).

View File

@ -21,10 +21,14 @@ module.exports = function(wss, server){
msg.headers[i] = msg.headers[i] || val; // reattach headers msg.headers[i] = msg.headers[i] || val; // reattach headers
}); });
server.call(ws, msg, function(reply){ server.call(ws, msg, function(reply){
if(!ws || !ws.send){ return } if(!ws || !ws.send || !ws._socket || !ws._socket.writable){ return }
reply = reply || {}; reply = reply || {};
reply.wsrid = msg.wsrid; if(msg && msg.headers && msg.headers['ws-rid']){
ws.send(Gun.text.ify(reply)); (reply.headers = reply.headers || {})['ws-rid'] = msg.headers['ws-rid'];
}
console.log("socket out", reply);
try{ws.send(Gun.text.ify(reply));
}catch(e){} // juuuust in case.
}); });
}); });
ws.off = function(m){ ws.off = function(m){

View File

@ -19,11 +19,13 @@
var ws = this; var ws = this;
req.headers['gun-sid'] = ws.sid = ws.sid? ws.sid : req.headers['gun-sid']; req.headers['gun-sid'] = ws.sid = ws.sid? ws.sid : req.headers['gun-sid'];
ws.sub = ws.sub || gun.server.on('network').event(function(msg){ ws.sub = ws.sub || gun.server.on('network').event(function(msg){
if(!ws || !ws.send){ return this.off() } if(!ws || !ws.send || !ws._socket || !ws._socket.writable){ return this.off() }
if(!msg || (msg.headers && msg.headers['gun-sid'] === ws.sid)){ return } if(!msg || (msg.headers && msg.headers['gun-sid'] === ws.sid)){ return }
delete msg.wsrid; // depreciate this! if(msg && msg.headers){ delete msg.headers['ws-rid'] }
ws.send(Gun.text.ify(msg)); try{ws.send(Gun.text.ify(msg));
}catch(e){} // juuuust in case.
}); });
console.log("socket in", req);
gun.__.opt.hooks.transport(req, res); gun.__.opt.hooks.transport(req, res);
}); });
gun.__.opt.ws.port = port || opt.ws.port || gun.__.opt.ws.port || 80; gun.__.opt.ws.port = port || opt.ws.port || gun.__.opt.ws.port || 80;
@ -110,7 +112,6 @@
//Gun.log("transport.loading key ->", key, gun.__.graph, gun.__.keys); //Gun.log("transport.loading key ->", key, gun.__.graph, gun.__.keys);
gun.load(key, function(err, node){ gun.load(key, function(err, node){
//tran.sub.scribe(req.tab, node._[Gun._.soul]); //tran.sub.scribe(req.tab, node._[Gun._.soul]);
// TODO: MAJOR BUG!!!! This is crashing server if the socket gets disconnected or something.
cb({headers: reply.headers, body: (err? (err.err? err : {err: err || "Unknown error."}) : node || null)}); cb({headers: reply.headers, body: (err? (err.err? err : {err: err || "Unknown error."}) : node || null)});
}); });
} }

View File

@ -416,6 +416,7 @@ describe('Gun', function(){
it('load blank kick get when it already exists', function(done){ it('load blank kick get when it already exists', function(done){
gun.load("some/empty/thing").blank(function(){ gun.load("some/empty/thing").blank(function(){
console.log("OH NO!", this._);
this.set({now: 'THIS SHOULD NOT HAPPEN'}); this.set({now: 'THIS SHOULD NOT HAPPEN'});
}).get(function(val){ }).get(function(val){
expect(val.now).to.be('exists'); expect(val.now).to.be('exists');
@ -423,13 +424,21 @@ describe('Gun', function(){
}); });
}); });
it('set path get sub', function(done){
gun.set({last: {some: 'object'}}).path('last').get(function(val){
expect(val.some).to.be('object');
done();
});
});
it('load set null', function(done){ it('load set null', function(done){
gun.set({last: {some: 'object'}}).path('last').get(function(val){ gun.set({last: {some: 'object'}}).path('last').get(function(val){
console.log("GET LAST FIRST", val, this._); expect(val.some).to.be('object');
}).set(null, function(err){ }).set(null, function(err){
console.log("ERR?", err); //console.log("ERR?", err);
}).get(function(val){ }).get(function(val){
console.log('GET', val); expect(val).to.be(null);
done();
}); });
}); });
@ -469,5 +478,29 @@ describe('Gun', function(){
done(); done();
}); });
}); });
it('set partial sub merge', function(done){
var mark = gun.set({name: "Mark", wife: { name: "Amber" }}).key('person/mark').get(function(mark){
expect(mark.name).to.be("Mark");
});
mark.set({age: 23, wife: {age: 23}});
setTimeout(function(){
mark.set({citizen: "USA", wife: {citizen: "USA"}}).get(function(mark){
expect(mark.name).to.be("Mark");
expect(mark.age).to.be(23);
expect(mark.citizen).to.be("USA");
this.path('wife').get(function(Amber){
expect(Amber.name).to.be("Amber");
expect(Amber.age).to.be(23);
expect(Amber.citizen).to.be("USA");
done();
});
});
}, 100);
});
}); });
}); });

12
test/tmp.js Normal file
View File

@ -0,0 +1,12 @@
(function(){
return; // this file is for temporary testings and shouldn't get run.
var Gun = require('../gun');
var gun = Gun();
Gun.log.verbose = true;
gun.set({foo: {bar: "lol"}}).path("foo", function(err, node, field){
console.log("on the path:", node, field)
}).get(function(val){
console.log("got:", val);
});
}());