mirror of
https://github.com/amark/gun.git
synced 2025-03-30 15:08:33 +00:00
clean up timing problems
This commit is contained in:
parent
3e6fc92949
commit
f8e4196dc9
86
gun.js
86
gun.js
@ -120,7 +120,7 @@
|
||||
Gun.obj.map(delta, function update(incoming, field){
|
||||
if(field === Gun._.meta){ return }
|
||||
var ctx = {incoming: {}, current: {}}, state;
|
||||
ctx.drift = Gun.time.is();
|
||||
ctx.drift = (ctx.drift = Gun.time.is()) > (Gun.time.now.last || -Infinity)? ctx.drift : Gun.time.now.last;
|
||||
ctx.incoming.value = Gun.is.soul(incoming) || incoming;
|
||||
ctx.current.value = Gun.is.soul(vertex[field]) || vertex[field];
|
||||
ctx.incoming.state = Gun.num.is(ctx.tmp = ((delta._||{})[Gun._.HAM]||{})[field])? ctx.tmp : -Infinity;
|
||||
@ -207,6 +207,7 @@
|
||||
if(opt === null){ return gun }
|
||||
opt = opt || {};
|
||||
gun.__.opt = gun.__.opt || {};
|
||||
gun.__.flag = gun.__.flag || {};
|
||||
gun.__.keys = gun.__.keys || {};
|
||||
gun.__.graph = gun.__.graph || {};
|
||||
gun.__.on = gun.__.on || Gun.on.create();
|
||||
@ -232,17 +233,17 @@
|
||||
gun.__ = from.__;
|
||||
gun._ = {on: Gun.on.create()};
|
||||
gun._.status = function(e){
|
||||
var proxy = function(chain, cb){
|
||||
var proxy = function(chain, cb, i){
|
||||
return Gun.obj.map(gun._.graph, function(on, soul){
|
||||
setImmediate(function(){ cb.call(on, on.status) });
|
||||
return on; // TODO: BUG! What about plural graphs?
|
||||
}) || gun._.on(e)[chain](function(status){
|
||||
if(status){ (gun._.graph = gun._.graph || {})[status.soul] = this }
|
||||
cb.call(this, this.status = status);
|
||||
});
|
||||
}, i);
|
||||
}
|
||||
proxy.event = function(cb){ return proxy('event', cb) };
|
||||
proxy.once = function(cb){ return proxy('once', cb) };
|
||||
proxy.event = function(cb, i){ return proxy('event', cb, i) };
|
||||
proxy.once = function(cb, i){ return proxy('once', cb, i) };
|
||||
proxy.emit = function(){
|
||||
var args = arguments;
|
||||
setImmediate(function(me){ (me = gun._.on(e)).emit.apply(me, args) })
|
||||
@ -266,19 +267,16 @@
|
||||
} else { load(key) } // not in memory
|
||||
} else
|
||||
if(ctx.key){
|
||||
|
||||
(function foo(){ // TODO: JANKY! UGLY!!!! Can resolve as soon as the object exists.
|
||||
if(ctx.node = gun.__.keys[ctx.key]){ // in memory, or from put.key
|
||||
if(true === ctx.node){
|
||||
setTimeout(foo,0);
|
||||
} else {
|
||||
cb.call(gun, null, Gun.obj.copy(ctx.node));
|
||||
var soul = Gun.is.soul.on(ctx.node);
|
||||
gun._.status('soul').emit({soul: soul});
|
||||
gun._.status('node').emit({soul: soul});
|
||||
}
|
||||
} else { load(key) } // not in memory
|
||||
})();
|
||||
function get(soul){
|
||||
if(!(soul = Gun.is.soul.on(ctx.node = gun.__.keys[ctx.key]))){ return }
|
||||
cb.call(gun, null, Gun.obj.copy(ctx.node));
|
||||
gun._.status('soul').emit({soul: soul});
|
||||
gun._.status('node').emit({soul: soul});
|
||||
}
|
||||
if(gun.__.keys[ctx.key]){ get() } // in memory
|
||||
else if(ctx.flag = gun.__.flag[key]){ // will be in memory
|
||||
ctx.flag.once(get);
|
||||
} else { load(key) } // not in memory
|
||||
|
||||
} else { cb.call(gun, {err: Gun.log("No key or relation to get!")}) }
|
||||
|
||||
@ -308,11 +306,11 @@
|
||||
if(!key){ return cb.call(gun, {err: Gun.log('No key!')}), gun }
|
||||
cb = cb || function(){};
|
||||
opt = opt || {};
|
||||
gun.__.keys[key] = true;
|
||||
(gun.__.flag[key] = gun._.status('node')).once(function($){
|
||||
gun.__.keys[key] = gun.__.graph[$.soul];
|
||||
delete gun.__.flag[key];
|
||||
}, -1);
|
||||
gun._.status('soul').event(function($){ // TODO: once per soul in graph. (?)
|
||||
gun._.status('node').once(function($){
|
||||
gun.__.keys[key] = gun.__.graph[$.soul];
|
||||
});
|
||||
if(Gun.fns.is(ctx.hook = gun.__.opt.hooks.key)){
|
||||
ctx.hook(key, $.soul, function(err, data){
|
||||
return cb.call(gun, err, data);
|
||||
@ -374,7 +372,6 @@
|
||||
var node = gun.__.graph[$.soul], field = Gun.text.ify(path.shift()), val;
|
||||
if(path.length){
|
||||
if(Gun.is.soul(val = node[field])){
|
||||
//root.console.log('path RECURSION', field);
|
||||
gun.get(val, function(err, data){
|
||||
if(err){ return cb.call(gun, err, data) }
|
||||
if(!data){ return cb.call(gun, null) }
|
||||
@ -444,7 +441,7 @@
|
||||
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){ // handle case where val is a gun context!
|
||||
var gun = this.chain(), drift = Gun.time.is();
|
||||
var gun = this.chain(), drift = Gun.time.now();
|
||||
cb = cb || function(){};
|
||||
opt = opt || {};
|
||||
|
||||
@ -454,7 +451,7 @@
|
||||
}
|
||||
gun.back._.status('soul').event(function($){ // TODO: maybe once per soul?
|
||||
var ctx = {}, obj = val, $ = Gun.obj.copy($);
|
||||
console.log("chain.put", val, $);
|
||||
console.log("chain.put", val);
|
||||
if(Gun.is.value(obj)){
|
||||
if($.from && $.at){
|
||||
$.soul = $.from;
|
||||
@ -485,14 +482,14 @@
|
||||
}
|
||||
if(!Gun.is.soul.on(at.node)){
|
||||
if(obj === at.obj){
|
||||
env.graph[at.node._[Gun._.soul] = $.soul] = at.node;
|
||||
cb(at, $.soul);
|
||||
env.graph[at.node._[Gun._.soul] = at.soul = $.soul] = at.node;
|
||||
cb(at, at.soul);
|
||||
} else {
|
||||
$.empty? path() : gun.back.path(at.path.join('.'), path); // TODO: clean this up.
|
||||
function path(err, data){
|
||||
var soul = Gun.is.soul.on(data) || Gun.roulette.call(gun);
|
||||
env.graph[at.node._[Gun._.soul] = soul] = at.node;
|
||||
cb(at, soul);
|
||||
at.soul = Gun.is.soul.on(data) || Gun.is.soul.on(at.obj) || Gun.roulette.call(gun);
|
||||
env.graph[at.node._[Gun._.soul] = at.soul] = at.node;
|
||||
cb(at, at.soul);
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -506,8 +503,8 @@
|
||||
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($.from = Gun.is.soul(ify.root[$.field])){ $.soul = $.from; $.field = null }
|
||||
gun._.on('soul').emit({soul: $.soul, field: $.field, candy: true});
|
||||
gun._.on('node').emit({soul: $.soul, field: $.field, barf: false});
|
||||
gun._.on('soul').emit({soul: $.soul, field: $.field});
|
||||
gun._.on('node').emit({soul: $.soul, field: $.field});
|
||||
if(Gun.fns.is(ctx.hook = gun.__.opt.hooks.put)){
|
||||
ctx.hook(ify.graph, 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) }
|
||||
@ -669,6 +666,11 @@
|
||||
}
|
||||
Util.time = {};
|
||||
Util.time.is = function(t){ return t? t instanceof Date : (+new Date().getTime()) }
|
||||
Util.time.now = function(t){
|
||||
return (t=t||Util.time.is()) > (Util.time.now.last || -Infinity)? (Util.time.now.last = t) : Util.time.now(t + 1);
|
||||
return (t = Util.time.is() + Math.random()) > (Util.time.now.last || -Infinity)?
|
||||
(Util.time.now.last = t) : Util.time.now();
|
||||
};
|
||||
}(Gun));
|
||||
;Gun.on=(function(){
|
||||
// events are fundamentally different, being synchronously 1 to N fan out,
|
||||
@ -790,9 +792,18 @@
|
||||
return end;
|
||||
}
|
||||
function map(ctx, cb){
|
||||
var rel = function(at, soul){
|
||||
at.soul = at.soul || soul;
|
||||
setImmediate(function(){ unique(ctx) },0);
|
||||
if(!at.back || !at.back.length){ return }
|
||||
Gun.list.map(at.back, function(rel){
|
||||
rel[Gun._.soul] = at.soul;
|
||||
});
|
||||
}, it;
|
||||
Gun.obj.map(ctx.at.obj, function(val, field){
|
||||
ctx.at.val = val;
|
||||
ctx.at.field = field;
|
||||
it = cb(ctx, rel) || true;
|
||||
if(field === Gun._.meta){
|
||||
ctx.at.node[field] = Gun.obj.copy(val); // TODO: BUG! Is this correct?
|
||||
return;
|
||||
@ -817,15 +828,8 @@
|
||||
} else {
|
||||
ctx.at.node[field] = Gun.obj.copy(val);
|
||||
}
|
||||
cb(ctx, function(at, soul){
|
||||
at.soul = at.soul || soul;
|
||||
setImmediate(function(){ unique(ctx) },0);
|
||||
if(!at.back || !at.back.length){ return }
|
||||
Gun.list.map(at.back, function(rel){
|
||||
rel[Gun._.soul] = at.soul;
|
||||
});
|
||||
});
|
||||
});
|
||||
if(!it){ cb(ctx, rel) }
|
||||
}
|
||||
function unique(ctx){
|
||||
if(ctx.err || !Gun.list.map(ctx.seen, function(at){
|
||||
@ -844,8 +848,8 @@
|
||||
} else {
|
||||
module.exports = Gun;
|
||||
}
|
||||
var setImmediate = setImmediate || function(cb){return setTimeout(cb,0)};
|
||||
var root = this || {}; // safe for window, global, root, and 'use strict'.
|
||||
root.setImmediate = root.setImmediate || function(cb){ return setTimeout(cb,0) };
|
||||
root.console = root.console || {log: function(s){ return s }}; // safe for old browsers
|
||||
var console = {log: Gun.log = function(s){return (Gun.log.verbose && root.console.log.apply(root.console, arguments)), s}};
|
||||
}({}));
|
||||
|
449
lib/api.js
449
lib/api.js
@ -1,449 +0,0 @@
|
||||
//var Gun = Gun || module.exports || require('../gun');
|
||||
var Gun = Gun || require('../gun');
|
||||
|
||||
Gun.chain.chain = function(from){
|
||||
var gun = Gun(null);
|
||||
from = from || this;
|
||||
gun.back = from;
|
||||
gun.__ = from.__;
|
||||
gun._ = {on: Gun.on.create() };
|
||||
return gun;
|
||||
}
|
||||
|
||||
Gun.chain.get = function(key, cb, opt){ // get opens up a reference to a node and loads it.
|
||||
var gun = this.chain(), ctx = {};
|
||||
if(!key){ return cb.call(gun, {err: Gun.log("No key or relation to get!") }), gun }
|
||||
ctx.key = Gun.text.is(key) && key; // if key is text, then key, else false.
|
||||
ctx.soul = Gun.is.soul(key); // if key is a soul, then the soul, else false.
|
||||
cb = cb || function(){};
|
||||
opt = opt || {};
|
||||
if(ctx.soul){
|
||||
Gun.fns.async(function(){ open(ctx.soul) });
|
||||
if(ctx.node = gun.__.graph[ctx.soul]){ // in memory
|
||||
cb.call(gun, null, Gun.obj.copy(ctx.node));
|
||||
} else { load(key) } // not in memory
|
||||
} else
|
||||
if(ctx.key){
|
||||
|
||||
(function foo(){ // TODO: UGLY!!!!
|
||||
if(ctx.node = gun.__.keys[ctx.key]){ // in memory, or from put.key
|
||||
if(true === ctx.node){
|
||||
Gun.fns.async(foo);
|
||||
} else {
|
||||
cb.call(gun, null, Gun.obj.copy(ctx.node));
|
||||
Gun.fns.async(function(){ open(Gun.is.soul.on(ctx.node)) });
|
||||
}
|
||||
} else { load(key) } // not in memory
|
||||
})();
|
||||
|
||||
} else { cb.call(gun, {err: Gun.log("No key or relation to get!")}) }
|
||||
|
||||
function open(soul){
|
||||
if(!soul || ctx.open){ return }
|
||||
ctx.open = true;
|
||||
gun._.on('soul').emit(soul);
|
||||
}
|
||||
function load(key){
|
||||
if(Gun.fns.is(ctx.hook = gun.__.opt.hooks.get)){
|
||||
ctx.hook(key, function(err, data){ // multiple times potentially
|
||||
//console.log("chain.get from load", err, data);
|
||||
if(err){ return cb.call(gun, err, data) }
|
||||
if(!data){ return cb.call(gun, null) } // TODO: will have have `not` be based on open?
|
||||
if(ctx.soul = Gun.is.soul.on(data)){
|
||||
open(ctx.soul);
|
||||
} else { return cb.call(gun, {err: Gun.log('No soul on data!') }, data) }
|
||||
if(err = Gun.union(gun, data).err){ return cb.call(gun, err) }
|
||||
cb.call(gun, null, data);
|
||||
}, opt);
|
||||
} else {
|
||||
root.console.log("Warning! You have no persistence layer to get from!");
|
||||
cb.call(gun, null, null); // Technically no error, but no way we can get data.
|
||||
}
|
||||
}
|
||||
return gun;
|
||||
}
|
||||
|
||||
Gun.chain.put = function(val, cb, opt){ // handle case where val is a gun context!
|
||||
var gun = this.chain(), drift = Gun.time.is(), flag;
|
||||
cb = cb || function(){};
|
||||
opt = opt || {};
|
||||
|
||||
if(!gun.back.back){
|
||||
gun = gun.chain();
|
||||
Gun.fns.async(function(){
|
||||
flag = true;
|
||||
gun.back._.on('soul').emit(Gun.is.soul.on(val) || Gun.roulette.call(gun));
|
||||
});
|
||||
}
|
||||
//gun.back._.on('soul').event(function(soul, field, from, at){
|
||||
Gun.when(gun.back, function(soul, field, from, at){
|
||||
console.log("chain.put", field, val, "on", soul, 'or', from, at);
|
||||
var ctx = {}, obj = val;
|
||||
if(Gun.is.value(obj)){
|
||||
if(from && at){
|
||||
soul = from;
|
||||
field = at;
|
||||
} // no else!
|
||||
if(!field){
|
||||
return cb.call(gun, {err: Gun.log("No field exists for " + (typeof obj) + "!")});
|
||||
} else
|
||||
if(gun.__.graph[soul]){
|
||||
ctx.tmp = {};
|
||||
ctx.tmp[ctx.field = field] = obj;
|
||||
obj = ctx.tmp;
|
||||
} else {
|
||||
return cb.call(gun, {err: Gun.log("No node exists to put " + (typeof obj) + " in!")});
|
||||
}
|
||||
}
|
||||
if(Gun.obj.is(obj)){
|
||||
if(field && !ctx.field){
|
||||
ctx.tmp = {};
|
||||
ctx.tmp[ctx.field = field] = obj;
|
||||
obj = ctx.tmp;
|
||||
}
|
||||
Gun.ify(obj, function(env, cb){
|
||||
var at;
|
||||
if(!env || !(at = env.at) || !env.at.node){ return }
|
||||
if(!at.node._){
|
||||
at.node._ = {};
|
||||
}
|
||||
if(!Gun.is.soul.on(at.node)){
|
||||
if(obj === at.obj){
|
||||
env.graph[at.node._[Gun._.soul] = soul] = at.node;
|
||||
cb(at, soul);
|
||||
} else {
|
||||
console.log('we are not at root, where are we at?', at);
|
||||
flag? path() : gun.back.path(at.path.join('.'), path);
|
||||
function path(err, data){
|
||||
var soul = Gun.is.soul.on(data) || Gun.roulette.call(gun);
|
||||
console.log("put pathing not root", soul, err, data);
|
||||
env.graph[at.node._[Gun._.soul] = soul] = at.node;
|
||||
cb(at, soul);
|
||||
};
|
||||
}
|
||||
}
|
||||
if(!at.node._[Gun._.HAM]){
|
||||
at.node._[Gun._.HAM] = {};
|
||||
}
|
||||
if(!at.field){ return }
|
||||
at.node._[Gun._.HAM][at.field] = drift;
|
||||
})(function(err, ify){
|
||||
console.log("chain.put PUT", ify.graph);
|
||||
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(from = Gun.is.soul(ify.root[field])){ soul = from; field = null }
|
||||
gun._.on('soul').emit(soul, field);
|
||||
if(Gun.fns.is(ctx.hook = gun.__.opt.hooks.put)){
|
||||
ctx.hook(ify.graph, 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) }
|
||||
return cb.call(gun, null, data);
|
||||
}, opt);
|
||||
} else {
|
||||
root.console.log("Warning! You have no persistence layer to save to!");
|
||||
cb.call(gun, null); // This is in memory success, hardly "success" at all.
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return gun;
|
||||
}
|
||||
|
||||
Gun.chain.key = function(key, cb, opt){
|
||||
var gun = this, ctx = {};
|
||||
if(!key){ return cb.call(gun, {err: Gun.log('No key!')}), gun }
|
||||
cb = cb || function(){};
|
||||
opt = opt || {};
|
||||
gun.__.keys[key] = true;
|
||||
//gun._.on('soul').event(function(soul){
|
||||
Gun.when(gun, function(soul){
|
||||
Gun.fns.async(function wait(node){ // TODO: UGLY!!! JANKY!!!
|
||||
node = gun.__.graph[soul];
|
||||
if(true === node){ return Gun.fns.async(wait) }
|
||||
gun.__.keys[key] = node;
|
||||
});
|
||||
if(Gun.fns.is(ctx.hook = gun.__.opt.hooks.key)){
|
||||
ctx.hook(key, soul, function(err, data){
|
||||
return cb.call(gun, err, data);
|
||||
}, opt);
|
||||
} else {
|
||||
root.console.log("Warning! You have no key hook!");
|
||||
cb.call(gun, null); // This is in memory success, hardly "success" at all.
|
||||
}
|
||||
});
|
||||
return gun;
|
||||
}
|
||||
|
||||
Gun.chain.path = function(path, cb){
|
||||
var gun = this.chain(), ctx = {};
|
||||
cb = cb || function(){};
|
||||
path = (Gun.text.ify(path) || '').split('.');
|
||||
//gun.back._.on('soul').event(function trace(soul){ // TODO: Check for field as well and merge?
|
||||
Gun.when(gun.back, function trace(soul){ // TODO: Check for field as well and merge?
|
||||
var node = gun.__.graph[soul], field = node && Gun.text.ify(path.shift()), val;
|
||||
console.log("path...", soul, field, node);
|
||||
if(!node){ // handle later
|
||||
return Gun.fns.async(function(){ // TODO: UGLY!!! JANKY!!!
|
||||
trace(soul);
|
||||
});
|
||||
} else
|
||||
if(path.length){
|
||||
if(Gun.is.soul(val = node[field])){
|
||||
gun.get(val, function(err, data){
|
||||
if(err){ return cb.call(gun, err, data) }
|
||||
if(!data){ return cb.call(gun, null) }
|
||||
trace(Gun.is.soul.on(data));
|
||||
});
|
||||
} else {
|
||||
cb.call(gun, null);
|
||||
}
|
||||
} else
|
||||
if(!Gun.obj.has(node, field)){ // TODO: THIS MAY NOT BE CORRECT BEHAVIOR!!!!
|
||||
cb.call(gun, null, null, field);
|
||||
gun._.on('soul').emit(soul, field); // if .put is after, makes sense. If anything else, makes sense to wait.
|
||||
} else
|
||||
if(Gun.is.soul(val = node[field])){
|
||||
gun.get(val, cb);
|
||||
gun._.on('soul').emit(Gun.is.soul(val), null, soul, field);
|
||||
} else {
|
||||
cb.call(gun, null, val, field);
|
||||
gun._.on('soul').emit(soul, field);
|
||||
}
|
||||
});
|
||||
|
||||
return gun;
|
||||
}
|
||||
|
||||
Gun.chain.on = function(cb){ // TODO: BUG! Major problem with events is that they won't re-trigger either if listened later.
|
||||
var gun = this, ctx = {};
|
||||
cb = cb || function(){};
|
||||
|
||||
Gun.when(gun, function(soul, field){
|
||||
gun.__.on(soul).event(function(delta){
|
||||
cb.call(gun, delta);
|
||||
});
|
||||
});
|
||||
|
||||
return gun;
|
||||
}
|
||||
|
||||
Gun.chain.val = function(cb){ // TODO: BUG! Major problem with events is that they won't re-trigger either if listened later.
|
||||
var gun = this, ctx = {};
|
||||
cb = cb || function(){};
|
||||
|
||||
Gun.when(gun, function(soul, field){
|
||||
Gun.fns.async(function wait(node){ // TODO: UGLY!!! JANKY!!!
|
||||
node = gun.__.graph[soul];
|
||||
if(!node || true === node){ return Gun.fns.async(wait) }
|
||||
cb.call(gun, field? node[field] : Gun.obj.copy(node));
|
||||
});
|
||||
});
|
||||
|
||||
return gun;
|
||||
}
|
||||
|
||||
Gun.when = function(gun, cb){ // how much memory will this consume?
|
||||
var setImmediate = setImmediate || function(cb){return setTimeout(cb,0)};
|
||||
Gun.obj.map(gun._.graph, function(on, soul){
|
||||
setImmediate(function(){ cb.apply(on, on.args) });
|
||||
});
|
||||
gun._.on('soul').event(function(soul){
|
||||
cb.apply((gun._.graph = gun._.graph || {})[this.soul = soul] = this, this.args = arguments);
|
||||
});
|
||||
}
|
||||
|
||||
Gun.union = function(gun, prime){
|
||||
var ctx = {};
|
||||
ctx.graph = gun.__.graph;
|
||||
if(!ctx.graph){ ctx.err = {err: Gun.log("No graph!") } }
|
||||
if(!prime){ ctx.err = {err: Gun.log("No data to merge!") } }
|
||||
if(ctx.soul = Gun.is.soul.on(prime)){
|
||||
ctx.tmp = {};
|
||||
ctx.tmp[ctx.soul] = prime;
|
||||
prime = ctx.tmp;
|
||||
}
|
||||
if(ctx.err){ return ctx }
|
||||
(function union(graph, prime){
|
||||
Gun.obj.map(prime, function(node, soul){
|
||||
soul = Gun.is.soul.on(node);
|
||||
if(!soul){ return }
|
||||
var vertex = graph[soul];
|
||||
if(!vertex){ // disjoint
|
||||
gun.__.on(soul).emit(graph[soul] = node); // TODO: BUG! We should copy the node in, not pass by reference?
|
||||
return;
|
||||
}
|
||||
Gun.HAM(vertex, node, function(){}, function(vertex, field, value){
|
||||
if(!vertex){ return }
|
||||
var change = {};
|
||||
change._ = change._ || {};
|
||||
change._[Gun._.soul] = Gun.is.soul.on(vertex);
|
||||
change._[Gun._.HAM] = change._[Gun._.HAM] || {};
|
||||
vertex[field] = change[field] = value;
|
||||
vertex._[Gun._.HAM][field] = change._[Gun._.HAM][field] = node._[Gun._.HAM][field];
|
||||
//context.nodes[change._[Gun._.soul]] = change;
|
||||
//context('change').fire(change);
|
||||
}, function(){
|
||||
|
||||
});
|
||||
});
|
||||
})(ctx.graph, prime);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
Gun.HAM = function(vertex, delta, lower, each, upper){
|
||||
var ctx = {};
|
||||
Gun.obj.map(delta, function update(incoming, field){
|
||||
if(field === Gun._.meta){ return }
|
||||
if(!Gun.obj.has(vertex, field)){ // does not need to be applied through HAM
|
||||
each.call({incoming: true, converge: true}, vertex, field, incoming);
|
||||
}
|
||||
var drift = Gun.time.is();
|
||||
var value = Gun.is.soul(incoming) || incoming;
|
||||
var current = Gun.is.soul(vertex[field]) || vertex[field];
|
||||
// TODO! BUG: Check for state existence so we don't crash if it isn't there. Maybe do this in union?
|
||||
var state = HAM(drift, delta._[Gun._.HAM][field], vertex._[Gun._.HAM][field], value, current);
|
||||
//console.log("the server state is",drift,"with delta:current",delta._[Gun._.HAM][field],vertex._[Gun._.HAM][field]);
|
||||
//console.log("having incoming value of",value,'and',current);
|
||||
if(state.err){
|
||||
root.console.log(".!HYPOTHETICAL AMNESIA MACHINE ERR!.", state.err); // this error should never happen.
|
||||
return;
|
||||
}
|
||||
if(state.state || state.quarantineState || state.current){
|
||||
lower.call(state, vertex, field, incoming);
|
||||
return;
|
||||
}
|
||||
if(state.incoming){
|
||||
each.call(state, vertex, field, incoming);
|
||||
return;
|
||||
}
|
||||
if(state.amnesiaQuarantine){
|
||||
ctx.up += 1;
|
||||
Gun.schedule(delta._[Gun._.HAM][field], function(){ // TODO: BUG!!! Don't hardcode this!
|
||||
update(incoming, field);
|
||||
ctx.up -= 1;
|
||||
upper.call(state, vertex, field, incoming);
|
||||
});
|
||||
}
|
||||
});
|
||||
function HAM(machineState, incomingState, currentState, incomingValue, currentValue){ // TODO: Lester's comments on roll backs could be vulnerable to divergence, investigate!
|
||||
if(machineState < incomingState){
|
||||
// the incoming value is outside the boundary of the machine's state, it must be reprocessed in another state.
|
||||
return {amnesiaQuarantine: true};
|
||||
}
|
||||
if(incomingState < currentState){
|
||||
// the incoming value is within the boundary of the machine's state, but not within the range.
|
||||
return {quarantineState: true};
|
||||
}
|
||||
if(currentState < incomingState){
|
||||
// the incoming value is within both the boundary and the range of the machine's state.
|
||||
return {converge: true, incoming: true};
|
||||
}
|
||||
if(incomingState === currentState){
|
||||
if(incomingValue === currentValue){ // Note: while these are practically the same, the deltas could be technically different
|
||||
return {state: true};
|
||||
}
|
||||
/*
|
||||
The following is a naive implementation, but will always work.
|
||||
Never change it unless you have specific needs that absolutely require it.
|
||||
If changed, your data will diverge unless you guarantee every peer's algorithm has also been changed to be the same.
|
||||
As a result, it is highly discouraged to modify despite the fact that it is naive,
|
||||
because convergence (data integrity) is generally more important.
|
||||
Any difference in this algorithm must be given a new and different name.
|
||||
*/
|
||||
if(String(incomingValue) < String(currentValue)){ // String only works on primitive values!
|
||||
return {converge: true, current: true};
|
||||
}
|
||||
if(String(currentValue) < String(incomingValue)){ // String only works on primitive values!
|
||||
return {converge: true, incoming: true};
|
||||
}
|
||||
}
|
||||
return {err: "you have not properly handled recursion through your data or filtered it as JSON"};
|
||||
}
|
||||
}
|
||||
|
||||
Gun.ify = (function(){
|
||||
function ify(data, cb, opt){
|
||||
console.log("=================================================================");
|
||||
//Gun.log.verbose = true;
|
||||
opt = opt || {};
|
||||
cb = cb || function(env, cb){ cb(env.at, Gun.roulette()) };
|
||||
var ctx = {}, end = function(fn){
|
||||
Gun.fns.async(function wait(){ // TODO: clean this up, possibly?
|
||||
if(ctx.err || !Gun.list.map(ctx.seen, function(at){
|
||||
if(!at.soul){ return true }
|
||||
})){
|
||||
fn(ctx.err, ctx)
|
||||
} else {
|
||||
Gun.fns.async(wait); // TODO: BUG! JANKY!!! Make this cleaner.
|
||||
}
|
||||
});
|
||||
}
|
||||
if(!data){ return ctx.err = Gun.log('Serializer does not have correct parameters.'), end }
|
||||
ctx.at = {};
|
||||
ctx.root = {};
|
||||
ctx.graph = {};
|
||||
ctx.queue = [];
|
||||
ctx.seen = [];
|
||||
ctx.loop = true;
|
||||
|
||||
ctx.at.path = [];
|
||||
ctx.at.obj = data;
|
||||
ctx.at.node = ctx.root;
|
||||
while(ctx.loop && !ctx.err){
|
||||
seen(ctx, ctx.at);
|
||||
map(ctx, cb);
|
||||
if(ctx.queue.length){
|
||||
ctx.at = ctx.queue.shift();
|
||||
} else {
|
||||
ctx.loop = false;
|
||||
}
|
||||
}
|
||||
return end;
|
||||
}
|
||||
function map(ctx, cb){
|
||||
console.log("scanning", Object.keys(ctx.at.obj));
|
||||
Gun.obj.map(ctx.at.obj, function(val, field){
|
||||
ctx.at.val = val;
|
||||
ctx.at.field = field;
|
||||
//(ctx.at.path = ctx.at.path || [field]); // TODO: BUG! Do later.
|
||||
if(field === Gun._.meta){
|
||||
ctx.at.node[field] = Gun.obj.copy(val); // TODO: BUG! Is this correct?
|
||||
return;
|
||||
}
|
||||
if(false && notValidField(field)){ // TODO: BUG! Do later for ACID "consistency" guarantee.
|
||||
return ctx.err = {err: Gun.log('Invalid field name on ' + ctx.at.path.join('.'))};
|
||||
}
|
||||
if(!Gun.is.value(val)){
|
||||
var at = {obj: val, node: {}, back: [], path: [field]}, tmp = {}, was;
|
||||
at.path = (ctx.at.path||[]).concat(at.path || []);
|
||||
if(!Gun.obj.is(val)){
|
||||
return ctx.err = {err: Gun.log('Invalid value at ' + at.path.join('.') + '!' )};
|
||||
}
|
||||
if(was = seen(ctx, at)){
|
||||
tmp[Gun._.soul] = Gun.is.soul.on(was.node) || null;
|
||||
(was.back = was.back || []).push(ctx.at.node[field] = tmp);
|
||||
} else {
|
||||
ctx.queue.push(at);
|
||||
tmp[Gun._.soul] = null;
|
||||
at.back.push(ctx.at.node[field] = tmp);
|
||||
}
|
||||
} else {
|
||||
ctx.at.node[field] = val; // TODO: BUG? the soul could be passed as ref, is that okay?
|
||||
}
|
||||
cb(ctx, function(at, soul){
|
||||
at.soul = at.soul || soul;
|
||||
if(!at.back || !at.back.length){ return }
|
||||
Gun.list.map(at.back, function(rel){ // TODO: BUG? sync issues?
|
||||
rel[Gun._.soul] = at.soul;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
function seen(ctx, at){
|
||||
var log = []; ctx.seen.forEach(function(val){ log.push(Object.keys(val.obj)) });
|
||||
//console.log('have we seen it yet?\n', at.obj, '\n = \n', log, '\n---------');
|
||||
return Gun.list.map(ctx.seen, function(has){
|
||||
if(at.obj === has.obj){ return has }
|
||||
}) || (ctx.seen.push(at) && false);
|
||||
}
|
||||
return ify;
|
||||
}({}));
|
@ -588,7 +588,7 @@ describe('Gun', function(){
|
||||
var prime = {
|
||||
'asdf': {
|
||||
_: {'#': 'asdf', '>':{
|
||||
x: Date.now() + (100) // above now or upper boundary, aka future.
|
||||
x: Date.now() + (200) // above now or upper boundary, aka future.
|
||||
}},
|
||||
x: 'how are you?'
|
||||
}
|
||||
@ -597,7 +597,7 @@ describe('Gun', function(){
|
||||
expect(gun.__.graph['asdf'].x).to.be('hello');
|
||||
var now = Date.now();
|
||||
var ctx = Gun.union(gun, prime, function(){
|
||||
expect(Date.now() - now).to.be.above(75);
|
||||
expect(Date.now() - now).to.be.above(100);
|
||||
expect(gun.__.graph['asdf'].x).to.be('how are you?');
|
||||
done();
|
||||
});
|
||||
@ -607,7 +607,7 @@ describe('Gun', function(){
|
||||
var prime = {
|
||||
'asdf': {
|
||||
_: {'#': 'asdf', '>':{
|
||||
y: Date.now() + (100) // above now or upper boundary, aka future.
|
||||
y: Date.now() + (200) // above now or upper boundary, aka future.
|
||||
}},
|
||||
y: 'goodbye'
|
||||
}
|
||||
@ -616,7 +616,7 @@ describe('Gun', function(){
|
||||
expect(gun.__.graph['asdf'].y).to.not.be.ok();
|
||||
var now = Date.now();
|
||||
var ctx = Gun.union(gun, prime, function(){
|
||||
expect(Date.now() - now).to.be.above(75);
|
||||
expect(Date.now() - now).to.be.above(100);
|
||||
expect(gun.__.graph['asdf'].y).to.be('goodbye');
|
||||
done();
|
||||
});
|
||||
@ -627,7 +627,7 @@ describe('Gun', function(){
|
||||
'asdf': {
|
||||
_: {'#': 'asdf', '>':{
|
||||
y: Date.now() + (2), // above now or upper boundary, aka future.
|
||||
z: Date.now() + (100) // above now or upper boundary, aka future.
|
||||
z: Date.now() + (200) // above now or upper boundary, aka future.
|
||||
}},
|
||||
y: 'bye',
|
||||
z: 'who'
|
||||
@ -638,7 +638,7 @@ describe('Gun', function(){
|
||||
expect(gun.__.graph['asdf'].z).to.not.be.ok();
|
||||
var now = Date.now();
|
||||
var ctx = Gun.union(gun, prime, function(){
|
||||
expect(Date.now() - now).to.be.above(75);
|
||||
expect(Date.now() - now).to.be.above(100);
|
||||
expect(gun.__.graph['asdf'].y).to.be('bye');
|
||||
expect(gun.__.graph['asdf'].z).to.be('who');
|
||||
done();
|
||||
@ -651,7 +651,7 @@ describe('Gun', function(){
|
||||
_: {'#': 'asdf', '>':{
|
||||
w: Date.now() + (2), // above now or upper boundary, aka future.
|
||||
x: Date.now() - (60 * 1000), // above now or upper boundary, aka future.
|
||||
y: Date.now() + (100), // above now or upper boundary, aka future.
|
||||
y: Date.now() + (200), // above now or upper boundary, aka future.
|
||||
z: Date.now() + (50) // above now or upper boundary, aka future.
|
||||
}},
|
||||
w: true,
|
||||
@ -667,7 +667,7 @@ describe('Gun', function(){
|
||||
expect(gun.__.graph['asdf'].z).to.be('who');
|
||||
var now = Date.now();
|
||||
var ctx = Gun.union(gun, prime, function(){
|
||||
expect(Date.now() - now).to.be.above(75);
|
||||
expect(Date.now() - now).to.be.above(100);
|
||||
expect(gun.__.graph['asdf'].w).to.be(true);
|
||||
expect(gun.__.graph['asdf'].x).to.be('how are you?');
|
||||
expect(gun.__.graph['asdf'].y).to.be('farewell');
|
||||
@ -785,7 +785,7 @@ describe('Gun', function(){
|
||||
expect(done.err).to.be.ok();
|
||||
expect(done.flag).to.not.be.ok();
|
||||
done();
|
||||
}, 150);
|
||||
}, 500);
|
||||
});
|
||||
|
||||
/*
|
||||
@ -1003,9 +1003,7 @@ describe('Gun', function(){
|
||||
it('get put null', function(done){
|
||||
gun.put({last: {some: 'object'}}).path('last').val(function(val){
|
||||
expect(val.some).to.be('object');
|
||||
}).put(null, function(err){
|
||||
//console.log("ERR?", err);
|
||||
}).val(function(val){
|
||||
}).put(null).val(function(val){
|
||||
expect(val).to.be(null);
|
||||
done();
|
||||
});
|
||||
@ -1019,7 +1017,7 @@ describe('Gun', function(){
|
||||
expect(val).to.be('bar'); // this should work
|
||||
done();
|
||||
});
|
||||
}, 100);
|
||||
}, 500);
|
||||
});
|
||||
|
||||
it('var get path', function(done){ // contexts should be able to be saved to a variable
|
||||
@ -1030,7 +1028,7 @@ describe('Gun', function(){
|
||||
expect(val).to.be('bar'); // this should work
|
||||
done();
|
||||
});
|
||||
}, 100);
|
||||
}, 500);
|
||||
});
|
||||
|
||||
it('get not put val path val', function(done){
|
||||
@ -1038,7 +1036,7 @@ describe('Gun', function(){
|
||||
return this.put({
|
||||
id: 'foobar',
|
||||
title: 'awesome title',
|
||||
todos: {hi: 'you'} // TODO: BUG! This should be empty?
|
||||
todos: {}
|
||||
}).key("examples/list/foobar");
|
||||
}).val(function(data){
|
||||
expect(data.id).to.be('foobar');
|
||||
@ -1126,7 +1124,7 @@ describe('Gun', function(){
|
||||
done();
|
||||
});
|
||||
});
|
||||
}, 50);
|
||||
}, 500);
|
||||
});
|
||||
|
||||
it('path path', function(done){
|
||||
@ -1198,6 +1196,48 @@ describe('Gun', function(){
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('val path put val', function(done){
|
||||
|
||||
var gun = Gun();
|
||||
|
||||
var al = gun.put({gender:'m', age:30, name:'alfred'}).key('user/alfred');
|
||||
var beth = gun.put({gender:'f', age:22, name:'beth' }).key('user/beth');
|
||||
|
||||
al.val(function(a){
|
||||
beth.path('friend').put(a).val(function(aa){
|
||||
expect(Gun.is.soul.on(a)).to.be(Gun.is.soul.on(aa));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
gun.put({gender:'m', age:30, name:'alfred'}).key('user/alfred');
|
||||
gun.put({gender:'f', age:22, name:'beth' }).key('user/beth');
|
||||
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){
|
||||
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.cat').key('the/cat');
|
||||
|
||||
gun.get('the/cat').val(function(c){
|
||||
expect(c.name).to.be('fluffy');
|
||||
expect(c.age).to.be(3);
|
||||
expect(c.coat).to.be('tabby');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return;
|
||||
it('map', function(done){
|
||||
var c = 0, map = gun.put({a: {here: 'you'}, b: {go: 'dear'}, c: {sir: '!'} });
|
||||
|
Loading…
x
Reference in New Issue
Block a user