60+ tests passing

This commit is contained in:
Mark Nadal 2016-07-26 22:46:15 -07:00
parent 78460bf5a5
commit a609584eda
2 changed files with 446 additions and 259 deletions

613
gun.js
View File

@ -27,7 +27,7 @@
var Type = {};
Type.fns = Type.fn = {is: function(fn){ return (!!fn && fn instanceof Function) }}
Type.bi = {is: function(b){ return (b instanceof Boolean || typeof b == 'boolean') }}
Type.num = {is: function(n){ return !list_is(n) && (Infinity === n || (n - parseFloat(n) + 1) >= 0) }}
Type.num = {is: function(n){ return !list_is(n) && ((n - parseFloat(n) + 1) >= 0 || Infinity === n || -Infinity === n) }}
Type.text = {is: function(t){ return (typeof t == 'string') }}
Type.text.ify = function(t){
if(Type.text.is(t)){ return t }
@ -289,10 +289,13 @@
var proto = create.prototype;
var on = create.on = On.scope();
proto.chain = function(tmp){
var chain = new this.constructor();
tmp = chain._ || (chain._ = {});
tmp.back = this;
proto.chain = function(){
var chain = new this.constructor(), _, tmp;
_ = chain._ || (chain._ = {});
_.back = this;
if(tmp = opt.extend){
_[tmp] = this._[tmp];
}
return chain;
}
proto.back = function(){
@ -681,10 +684,10 @@
}
function node(env, at){ var tmp;
if(tmp = seen(env, at)){ return tmp }
if(tmp = Node.soul(at.obj)){
/*if(tmp = Node.soul(at.obj)){ // TODO: You should probably delete this. Don't use it anymore. Maybe run one last test where a non-graphed document contains a graph and see if it has problems.
env.graph[at.soul = tmp] = at.node = at.obj;
return at;
}
}*/
if(Node.ify(at.obj, map, {env: env, at: at})){
env.graph[at.soul = Node.soul(at.node)] = at.node;
}
@ -692,6 +695,9 @@
}
function map(v,f,n){
var env = this.env, at = this.at, is = Val.is(v), tmp;
if(Node._ === f && obj_has(v,Val.rel._)){
return; // TODO: Bug?
}
if(env.map){
env.map(v,f,n);
}
@ -707,7 +713,12 @@
}
}
if(!f){
return at.node = Node.soul.ify({}, at.soul);
at.node = at.node || {};
if(obj_has(v, Node._)){
at.node._ = Gun.obj.copy(v._);
}
at.node = Node.soul.ify(at.node, at.soul);
return at.node;
}
if(is){
return v;
@ -757,7 +768,7 @@
}
}());
var fn_is = Type.fn.is;
var obj = Type.obj, obj_is = obj.is, obj_del = obj.del, obj_empty = obj.empty, obj_put = obj.put, obj_map = obj.map, obj_copy = obj.copy;
var obj = Type.obj, obj_is = obj.is, obj_del = obj.del, obj_has = obj.has, obj_empty = obj.empty, obj_put = obj.put, obj_map = obj.map, obj_copy = obj.copy;
module.exports = Graph;
/*setTimeout(function(){ // test
var g = Graph.ify({
@ -776,12 +787,13 @@
;module(function(module){
function Gun(o){
if(!(this instanceof Gun)){ return new Gun.create(o) }
if(!(this instanceof Gun)){ return Gun.create(o) }
this._ = {gun: this};
if(o){ this.opt(o) }
}
Gun.create = Gun;
Gun.create = function(o){
return new Gun().opt(o);
};
Gun._ = { // some reserved key words, these are not the only ones.
meta: '_' // all metadata of the node is stored in the meta property on the node.
@ -813,37 +825,95 @@
Gun.state = require('./state');
Gun.graph = require('./graph');
var opt = {chain: 'in', back: 'out', id: Gun._.soul};
var opt = {chain: 'in', back: 'out', extend: 'root', id: Gun._.soul};
Gun.chain = require('./chain')(Gun, opt);
Gun.chain.chain.opt = opt;
Gun.chain.opt = function(opt, stun){
opt = opt || {};
var gun = this, at = gun._, u;
if(!at.graph){
at.graph = {};
at.opt = at.opt || {};
;(function(){
Gun.chain.opt = function(opt){
opt = opt || {};
var gun = this, at = gun._, tmp, u;
if(!at.root){ root(at) }
tmp = at.opt = at.opt || {};
if(text_is(opt)){ opt = {peers: opt} }
else if(list_is(opt)){ opt = {peers: opt} }
if(text_is(opt.peers)){ opt.peers = [opt.peers] }
if(list_is(opt.peers)){ opt.peers = obj_map(opt.peers, function(n,f,m){m(n,{})}) }
obj_map(opt, function map(v,f){
if(obj_is(v)){
tmp = tmp[f] || (tmp[f] = {}); // TODO: Bug? Be careful of falsey values getting overwritten?
obj_map(v, map);
return;
}
tmp[f] = v;
});
return gun;
}
at.opt.uuid = opt.uuid || text_random;
at.opt.state = opt.state || Gun.state;
at.opt.peers = at.opt.peers || {};
if(text_is(opt)){ opt = {peers: opt} }
if(list_is(opt)){ opt = {peers: opt} }
if(text_is(opt.peers)){ opt.peers = [opt.peers] }
if(list_is(opt.peers)){ opt.peers = obj_map(opt.peers, function(n,f,m){m(n,{})}) }
obj_map(opt.peers, function(v, f){
at.opt.peers[f] = v;
});
obj_map(['key', 'path', 'map', 'not', 'init'], function(f){
if(!opt[f]){ return }
at.opt[f] = opt[f] || at.opt[f];
});
if(!stun){ Gun.on('opt', {gun: gun, opt: opt}) }
return gun;
}
function root(at){
var gun = at.gun;
at.root = gun;
at.graph = {};
gun.on('in', input, at);
gun.on('out', output, at);
}
function output(at){
var cat = this, gun = cat.gun, tmp;
console.debug(7, 'out!', at, cat);
if(at.put){
cat.on('in', at);
}
if(!at.gun){
at = Gun.obj.to(at, {gun: gun}); // TODO: BUG! Maybe we shouldn't do this yet since it will effect the redirected input stream?
}
if(at.put){ Gun.on('put', at) }
if(at.get){ get(at, cat) }
Gun.on('out', at);
if(!cat.back){ return }
cat.back.on('out', at);
}
function get(at, cat){
var soul = at.get[_soul], node = cat.graph[soul], field = at.get[_field];
if(node && (!field || obj_has(node, field))){
cat.on('in', {
put: Gun.graph.node(node), // TODO: BUG! Clone node!
get: soul
});
return;
}
Gun.on('get', at);
}
function input(at){ var cat = this;
if(!Gun.obj.is(at.put)){ return }
if(cat.graph){
Gun.obj.map(at.put, ham, {at: at, cat: this}); // all unions must happen first, sadly.
}
Gun.obj.map(at.put, map, {at: at, cat: this});
}
function ham(data, key){
var cat = this.cat, graph = cat.graph;
graph[key] = Gun.HAM.union(graph[key] || data, data) || graph[key];
}
function map(data, key){
var cat = this.cat, graph = cat.graph, path = cat.path || {}, gun, at;
if(!(gun = path[key])){ return }
(at = gun._).change = data;
if(graph){
data = graph[key]; // TODO! BUG/PERF! COPY!?
}
at.put = data; // TODO: Should be merged! Even for non-root level items.
gun.on('in',{
put: data,
get: key,
gun: gun,
via: this.at
});
}
}());
var text = Type.text, text_is = text.is, text_random = text.random;
var list_is = Type.list.is;
var obj_map = Type.obj.map;
var list = Type.list, list_is = list.is;
var obj = Type.obj, obj_is = obj.is, obj_has = obj.has, obj_map = obj.map;
var _soul = Gun._.soul, _field = Gun._.field;
console.debug = function(i, s){ return (console.debug.i && i === console.debug.i && console.debug.i++) && console.log.apply(console, arguments), s };
@ -964,7 +1034,7 @@
}
}
Gun.HAM.union = function(vertex, node, opt){
if(!node._ || !vertex._){ return }
if(!node || !vertex || !node._ || !vertex._){ return }
opt = num_is(opt)? {machine: opt} : {machine: (+new Date)};
opt.union = Gun.obj.copy(vertex);
opt.vertex = vertex;
@ -975,28 +1045,6 @@
}
return opt.union;
}
Gun.HAM.node = function(gun, node, opt){
if(!node){ return }
opt = num_is(opt)? {state: opt} : opt || {};
if(gun instanceof Gun){
var soul = node_soul(node), root = gun.Back(-1);
var graph = root._.graph || (root._.graph = {});
if(!soul){ return }
var vertex = graph[soul];
if(vertex === node){ return vertex }
vertex = graph[soul] = vertex || node_ify({}, soul);
var machine = opt.state || ((root._.opt||opt).state || Gun.state)();
} else {
var soul = node_soul(gun);
if(!soul){ return }
var vertex = gun;
if(vertex === node){ return vertex }
var machine = opt.state;
}
obj_map(node._, meta, vertex);
if(!node_is(node, map, {node:node,vertex:vertex,machine:machine,opt:opt})){ return }
return vertex;
}
var Type = Gun;
var num = Type.num, num_is = num.is;
var obj = Type.obj, obj_has = obj.has, obj_put = obj.put, obj_map = obj.map;
@ -1016,12 +1064,8 @@
;(function(){ var obj = {}, u;
Gun.chain.Back = function(n, opt){ var tmp;
if(-1 === n){
if(tmp = this.Back('graph', this)){
return tmp;
}
this._.graph = {};
return this;
if(-1 === n || Infinity === n){
return this._.root;
}
var gun = this, at = gun._;
if(typeof n === 'string'){
@ -1044,53 +1088,119 @@
}())
;(function(){
Gun.chain.put = function(data, cb, opt){
var gun = this, at = gun._, env, tmp;
if(!obj_is(data)){
cb({err: Gun.log("No node exists to put ", (typeof data), '"' + data + '" in!')});
return gun;
if(!from.back || !from.get || !from.get.field || !back.Back('get')){
return back;
Gun.chain.puut = function(data, cb, opt){
var gun = this, at = gun._, root = gun.Back(-1);
if(at.err){ return gun }
opt = opt || {};
opt.gun = gun;
opt.any = opt.any || cb;
opt.data = opt.data || data;
opt.state = opt.state || (gun.Back('opt.state') || Gun.state)();
if(root !== at.back){
opt.soul = opt.soul || Gun.node.soul(data);
if(root === gun){
opt.soul = opt.soul || (opt.uuid || gun.Back('opt.uuid') || Gun.text.random)();
}
from.back.put(obj.put({}, from.get.field, data), cb, opt); // TODO: Bug! Dynamic paths! Won't work.
return back;
if(opt.soul){
gun = root.get(opt.soul).put(data, cb, opt);
} else {
gun.on(save, {as: opt});
opt.next = Gun.on.next(gun);
}
return gun;
} else
if(!opt.soul){
if(!(opt.init || gun.Back('opt.init'))){
}
}
opt.soul = opt.soul || at.get || Gun.node.soul(data);
console.debug(11, 'put', root === at.back, opt.soul);
if(!opt.soul){
EXPLODE;
}
if(!obj_is(data)){
(cb||noop).call(gun, {err: Gun.log("No node exists to put ", (typeof data), '"' + data + '" in!')});
return gun;
}
env = opt.env = Gun.state.map(map, opt.state);
env.soul = opt.soul;
Gun.graph.ify(data, env, opt);
if(env.err){
cb({err: Gun.log(env.err)});
return gun;
}
if(opt.batch){
opt.batch(env.graph);
return gun;
}
return gun.on('out', {
gun: gun, put: env.graph, opt: opt,
'#': Gun.on.ask(function(ack, ev){ ev.off(); // One response is good enough for us currently. Later we may want to adjust this.
if(!opt.any){ return }
opt.any(ack.err, ack.ok);
}, opt)
});
}
Gun.chain.put = function(data, cb, opt){
var gun = this, at = gun._, env, root, tmp;
if(at.err){ return gun }
if(!obj_is(data)){
return path(gun, data, cb, opt);
}
opt = opt || {};
opt.gun = gun;
opt.any = opt.any || cb;
opt.data = opt.data || data;
opt.soul = opt.soul || Gun.node.soul(data);
opt.state = (opt.state || gun.Back('opt.state') || Gun.state)();
opt.state = opt.state || (gun.Back('opt.state') || Gun.state)();
root = gun.Back(-1);
if(!opt.soul){
if(at.get && (tmp = at.back) && (tmp = tmp._) && !tmp.get){
opt.soul = at.get;
} else
if(!gun.Back('get')){
if(root === gun){
opt.soul = (opt.uuid || gun.Back('opt.uuid') || Gun.text.random)();
} else {
opt.next = Gun.on.next(gun);
gun.on(save, {as: opt});
} else
if(at.get && root === at.back){
if(!(opt.init || gun.Back('opt.init'))){
opt.soul = at.get;
}
}
}
if(opt.soul){
gun = gun.Back(-1).get(opt.soul);
gun = root.get(opt.soul);
} else {
gun.on(save, {as: opt});
opt.next = Gun.on.next(gun);
return gun;
}
env = opt.env = Gun.state.map(map, opt.state); // TODO: Enforce states correctly! Something seemed to be lazy earlier.
env.soul = opt.soul;
Gun.graph.ify(data, env, opt); // TODO: Enforce states correctly! Something seemed to be lazy earlier.
Gun.graph.ify(data, env, opt);
if(env.err){
cb({err: Gun.log(env.err)});
return gun;
}
return gun.on('out', {put: env.graph,
return gun.on('out', {
gun: gun, put: env.graph, opt: opt,
'#': Gun.on.ask(function(ack, ev){ ev.off(); // One response is good enough for us currently. Later we may want to adjust this.
if(!opt.any){ return }
opt.any(ack.err, ack.ok);
}, opt)
});
};
function save(){
console.log("spooky");
function path(gun, data, cb, opt){
var at = gun._, tmp;
if(opt && opt.soul && at.get){
return gun.Back(-1).get(opt.soul).put(obj_put({}, at.get, data), cb, opt).get(at.get, null, {path: true});
}
if(!at.get || !(tmp = at.back) || !tmp._.get){ // TODO: BUG! Won't this fail on a `gun.get('foo').map().put('yes')` or with a `path('boo')` between map and put?
(cb||noop).call(gun, {err: Gun.log("No node exists to put ", (typeof data), '"' + data + '" in!')});
return gun;
}
if(at.get){
return at.back.put(obj_put({}, at.get, data), cb, opt).get(at.get, null, {path: true});
}
}
function save(at, ev){ var cat = this;
}
function map(v,f,n){ var opt = this;
if(!n){
@ -1214,12 +1324,14 @@
if(!opt || !opt.path){ var back = this.Back(-1); } // TODO: CHANGING API! Remove this line!
var gun, back = back || this;
var path = back._.path || empty, tmp;
console.debug(1, 'get', lex);
if(typeof lex === 'string'){
if(!(gun = path[lex])){
console.debug(2, 'get', lex);
gun = cache(lex, back);
}
} else
if(!lex && 0 != lex){ // TODO: BUG!?
if(!lex && 0 != lex){
(gun = back.chain())._.err = {err: Gun.log('Invalid get request!', lex)};
if(cb){ cb.call(gun, gun._.err) }
return gun;
@ -1247,55 +1359,53 @@
}
if(cb && cb instanceof Function){
(opt = opt || {}).any = cb;
opt.gun = opt.gun || gun;
Gun.chain.get.any(opt);
(opt.gun = opt.gun || gun).get.any(opt);
}
return gun;
}
function cache(key, back){
var cat = back._, path = cat.path, gun = back.chain(), at = gun._;
at.get = key;
if(!path){
if(cat.back && !cat.back._.path && !cat.graph){
cat.graph = {}; // TODO: DEPRECATE?
}
path = cat.path = {};
back.on('in', input, cat); // WAITS FOR DATA.
back.on('out', output, cat);
}
return path[at.get] = gun;
if(!path){ path = cat.path = {} }
path[at.get = key] = gun;
Gun.on('path', at);
gun.on('in', input, at); // For 'in' if I add my own listeners to each then I MUST do it before in gets called. If I listen globally for all incoming data instead though, regardless of individual listeners, I can transform the data there and then as well.
gun.on('out', output, at); // However for output, there isn't really the global option. I must listen by adding my own listener individually BEFORE this one is ever called.
console.debug(3, 'cache', key);
return gun;
}
function output(at){ var cat = this, tmp;
if(cat.graph){
if(at.put){
cat.on('in', at);
}
}
if(at.get){
var get = at.get;
function output(at){
var cat = this, gun = cat.gun, root = gun.Back(-1), put, get, tmp;
if((get = at.get)){
at = Gun.obj.to(at, {});
if(cat.graph){
if(typeof get === 'string'){
at.get = Gun.obj.put({}, Gun._.soul, get);
if(typeof get === 'string'){
get = at.get = Gun.obj.put({}, (root === cat.back? _soul : _field), get);
} else {
if(root === cat.back){
if(!get[_soul]){
get[_soul] = cat.get;
}
}
}
if(cat.put){
// CACHED RESULT! REPLY NOW!
}
}
console.debug(6, 'out', at, cat);
cat.back.on('out', at);
}
function input(at){ var cat = this;
cat.put = Gun.HAM.union(cat.put, cat.change = at.put) || cat.put || at.put;
if(!Gun.obj.is(cat.change)){ return }
Gun.obj.map(at.put, map, {at: at, cat: this});
}
function map(data, key){
var cat = this.cat, gun = cat.gun.get(key), at = gun._;
if(cat.graph){
cat.graph[key] = Gun.HAM.union(cat.graph[key] || data, data) || cat.graph[key] || data;
function input(at, ev){ var cat = this, tmp;
cat.put = at.put;
if(Gun.val.is(cat.change = cat.change || at.put)){
value.call(cat, at, ev);
return;
}
at.put = (cat.graph||cat.put||empty)[key] || data; // TODO: BUG! Is this okay?
if((tmp = cat.link) && (tmp = tmp.resume)){ // TODO: BUG!? Does this belong here or ABOVE the val.is check? I'm guessing here.
tmp(cat); // TODO: BUG! What about via? Do we need to clone this?
}
Gun.obj.map(cat.change, map, {at: at, cat: this, put: at.put});
}
function map(data, key){ // Map over only the changes on every update.
if(Gun._.meta === key){ return }
var cat = this.cat, path = cat.path || {}, gun;
if(!(gun = path[key])){ return }
if(this.put){ data = this.put[key] || data } // But use the actual data.
gun.on('in',{
put: data,
get: key,
@ -1303,18 +1413,40 @@
via: this.at
});
}
Gun.chain.get.any = function(opt){
opt.gun.on('in', any, opt);
if(opt.run){ return } // Useless if data is already known to be successful. We don't need to ask for anything.
opt.gun.on('out', {'#': Gun.on.ask(anything, opt), get: opt.gun._.get})
function value(at, ev){
var cat = this, rel = Gun.val.rel.is(cat.change), tmp, u;
if(!rel || rel === cat.rel){ return }
if(tmp = cat.link){
if((tmp = tmp.opt) && (tmp = tmp.event)){
tmp.off();
}
// TODO: BUG! PERF! If there are no other uses of cat.link.gun then we need to cat.link.gun.off() to clean it up from memory. This requires a system that knows how many things point to it though. Maybe `gun.off` should handle this logic using info in the @$ event?
}
cat.rel = rel;
cat.change = u;
cat.link = {opt: {change: true, as: cat}, resume: ev.stun(true)};
cat.link.gun = cat.gun.Back(-1).get(rel).on(input, cat.link.opt); // TODO: BUG!? Don't use `gun.on` so that way get/put don't depend upon `.chain.on`?
}
function any(at, ev){ var opt = this; opt.run = true;
Gun.chain.get.any = function(opt){
if(!opt || !opt.any){ return }
console.debug(4, 'any', opt);
opt.gun.on('in', any, opt);
console.debug(5, 'any ran', opt.ran);
if(opt.ran){ return } // Useless if data is already known to be successful. We don't need to ask for anything.
opt.gun.on('out', {
gun: opt.gun, get: opt.gun._.get,
'#': Gun.on.ask(ack, opt),
})
}
function any(at, ev){ var opt = this; opt.ran = true;
console.debug(9, 'nothing', at);
opt.any.call(at.gun || opt.gun, at.err, at.put, at.get, at, ev);
}
function anything(ack, ev){ var opt = this;
function ack(ack, ev){ var opt = this;
any.call(opt, {err: ack.err, put: ack.put}, ev);
}
var empty = {}, u;
var _soul = Gun._.soul, _field = Gun._.field;
}());
;(function(){
@ -1354,60 +1486,60 @@
return s;
}
keyed._ = '##';
var get = Gun.chain.get;
Gun.chain.get = function(lex, cb, opt){
var gun = get.call(this, lex, null, opt), at = gun._, cat = this._, tmp;
if(!at.key && cat.path && (tmp = cat.back) && !tmp._.path){
at.key = true;
gun.on('in', pseudo, at);
Gun.on('path', function(at){
var gun = at.gun;
if(gun.Back(-1) !== at.back){ return }
gun.on('in', pseudo, gun._);
gun.on('out', normalize, gun._);
});
function normalize(at){ var cat = this;
if(!at.put){
if(at.get){
search.call(cat, at);
}
return;
}
if(cb && cb instanceof Function){
(opt = opt || {}).any = cb;
opt.gun = opt.gun || gun;
Gun.chain.get.any(opt);
}
return gun;
}
Gun.chain.get.any = get.any;
function normalize(cat){
if(!cat || !cat.put){ return }
//console.log("normalize", cat.put);
return;
var at = cat, env = at.env;
if(at.opt.key){ return }
is_graph(env.graph, function(node, soul){ // TODO: CLEAN ALL OF THIS UP!
var key = {node: at.gun.__.graph[soul]}, tmp;
if(!obj_has(key.node, keyed.on)){ return } // TODO: BUG! Should iterate over it anyways to check for non #soul# properties to port.
is_node(key.node, function each(rel, s){
if(at.opt && at.opt.key){ return }
var put = at.put;
Gun.graph.is(put, function(node, soul){ // TODO: CLEAN ALL OF THIS UP!
var key = {node: cat.gun.Back(-1)._.graph[soul]}, tmp;
if(!obj_has(key.node, keyed._)){ return } // TODO: BUG! Should iterate over it anyways to check for non #soul# properties to port.
Gun.node.is(key.node, function each(rel, s){
if(!(s = keyed(s))){ return }
var n = at.gun.__.graph[s]; // TODO: BUG! Should we actually load the item or only use what is in memory?
if(obj_has(n, keyed.on)){
is_node(n, each);
var n = cat.gun.Back(-1)._.graph[s]; // TODO: BUG! Should we actually load the item or only use what is in memory?
if(obj_has(n, keyed._)){
Gun.node.is(n, each);
return;
}
rel = env.graph[s] = env.graph[s] || is_node_soul_ify({}, s);
is_node(node, function(v,f){
is_node_state_ify(rel, {field: f, value: v, state: is_node_state(node, f) });
rel = put[s] = put[s] || Gun.node.soul.ify({}, s);
Gun.node.is(node, function(v,f){
rel[f] = v;
Gun.state.ify(rel, f, Gun.state.is(node, f));
});
Gun.obj.del(env.graph, soul);
Gun.obj.del(put, soul);
});
});
}
function search(at){
delete at.get[Gun._.field]; // TODO: BUG!? Meh?
}
function pseudo(at, e){ var cat = this;
var put = at.put;
if(!put){ return }
if(!put[keyed._] && !cat.pseudo){ return }
var soul = Gun.node.soul(put), resume = e.stun(resume), change = cat.change || at.put, seen = cat.seen || (cat.seen = {}), data;
var change = cat.change;
if(!change || !at.put){ return }
if(!at.put[keyed._] && !cat.pseudo){ return }
var soul = Gun.node.soul(at.put), resume = e.stun(resume), gun = cat.gun, seen = cat.seen || (cat.seen = {}), already = true, data;
if(!cat.pseudo){
cat.pseudo = Gun.node.ify({}, soul);
cat.pseudo._.key = 'pseudo'; // TODO: CLEAN THIS UP!
cat.pseudo._['>'] = {}; // TODO: CLEAN THIS UP!
cat.put = cat.pseudo;
}
Gun.node.is(change, function(v, f){ var key;
if(key = keyed(f)){
if(seen[key]){ return}
if(seen[key]){ return }
already = false;
seen[key] = true; // TODO: Removing keys?
cat.gun.Back(-1).get(key).on(on);
cat.gun.Back(-1).get(key).on(on, true); // TODO: BUG! Perf! These are listeners that will become leaked! If we `gun.off()` on a pseudo we need to remember to also clean up these listeners.
return;
}
if(keyed._ === f){ return }
@ -1416,17 +1548,29 @@
Gun.state.ify(data, f, Gun.state.is(change, f));
});
function on(put){
// TODO: PERF? Only use change!
//put = this._.change || put;
cat.pseudo = Gun.HAM.union(cat.pseudo, put) || cat.pseudo;
cat.put = cat.pseudo;
resume(cat);
cat.change = put;
resume({
put: cat.pseudo,
get: soul
//via: this.at
});
}
if(!data){
if(already){
on(cat.pseudo);
}
return;
}
if(!data){ return }
var graph = {};
Gun.obj.map(seen, function(t,s){
graph[s] = Gun.node.soul.ify(Gun.obj.copy(data), s);
});
cat.gun.Back(-1).on('in', {put: graph});
}
var obj = Gun.obj, obj_has = obj.has;
}());
Gun.chain.path = function(field, cb, opt){
@ -1461,23 +1605,34 @@
;(function(){
Gun.chain.chain.opt.on = function(cb, opt){
if(!cb){ return this }
opt = opt || {};
var tmp;
opt = (true === opt)? {change: 1} : opt || {};
opt.gun = opt.gun || this;
opt.ok = cb;
opt.gun.on('in', ok, opt);
opt.event = opt.gun._.on('in', ok, opt);
if(opt.as && (tmp = opt.as.ons)){
(tmp['@$'] || (tmp['@$'] = {s: []})).s.push(opt.event);
}
if(!opt.run){
opt.gun.on('out', obj_to(opt.gun._));
opt.gun._.on('out', obj_to(opt.gun._));
}
return this;
}
function ok(cat, ev){ var opt = this; opt.run = true;
function ok(cat, ev){ var opt = this, data, tmp; opt.run = true;
// TODO: BUG! Need to use at.put > cat.put for merged cache?
if(!cat.put && !obj_has(cat, 'put')){ return }
if(!(data = cat.put) && !obj_has(cat, 'put')){ return } // TODO: what if cat.put is undefined? This shouldn't happen but might from how get's anything is coded.
if(tmp = opt.change){
if(1 === tmp){
opt.change = true;
} else {
data = opt.gun._.change;
}
}
if(opt.as){
opt.ok.call(opt.as, cat, ev);
} else {
opt.ok.call(cat.gun || opt.gun, cat.put, cat.get, cat, ev);
opt.ok.call(cat.gun || opt.gun, data, cat.get, cat, ev);
}
}
@ -1489,7 +1644,7 @@
}
if(cb){
(opt = opt || {}).ok = cb;
at.on(val, {as: opt}); // LOADS DATA
at.on(val, {as: opt}); // LOADS DATA // TODO: Have `.val` support an `opt.as` ability of its own?
}
return gun;
}
@ -1510,7 +1665,32 @@
ev.off();
opt.ok.call(cat.gun, data, cat.get); // TODO: BUG! opt.gun?
}
var obj = Gun.obj, obj_has = obj.has, obj_to = obj.to;
Gun.chain.off = function(){
var gun = this, at = gun._, tmp;
var back = at.back || {}, cat = back._;
if(!cat){ return }
if(tmp = cat.path){
if(tmp[at.get]){
obj_del(tmp, at.get);
} else {
obj_map(tmp, function(path, key){
if(gun !== path){ return }
obj_del(tmp, key);
});
}
}
if((tmp = gun.Back(-1)) === back){
obj_del(tmp.graph, at.get);
}
if(at.ons && (tmp = at.ons['@$'])){
obj_map(tmp.s, function(ev){
ev.off();
});
}
return gun;
}
var obj = Gun.obj, obj_has = obj.has, obj_del = obj.del, obj_to = obj.to;
}());
;(function(){
@ -1542,75 +1722,43 @@
;(function(){
var module = function(cb){ return function(){ cb({exports:{}}) } };
;module(function(module){
Gun.chain.wsp = function(){
var back = this, gun;
gun = back.chain();
gun._.wsp = true;
gun.on('out', function(at){
back.on('out', at);
if(!at.get){ return }
var peers = (at.gun || gun).Back('opt.peers');
if(!peers){
Gun.log.once('peers', "Warning! You have no peers to connect to!");
Gun.on.ack(at['#'], {});
return;
}
});
return gun.chain();
}
var create = Gun.create;
function wsp(o){
return new create(o).wsp();
}
Gun.create = wsp;
})(module, './src/WebSocket');
;module(function(module){
var root, noop = function(){};
if(typeof window !== 'undefined'){ root = window }
var store = root.localStorage || {setItem: noop, removeItem: noop, getItem: noop};
Gun.chain.local = function(opt){
var back = this, gun, chain;
opt = opt || {};
opt.prefix = opt.prefix || 'gun/';
gun = back.chain();
gun._.local = true;
gun.on('out', function(at){
if(at.get){ get(chain, at, opt) }
if(at.put){ put(chain, at, opt) }
back.on('out', at);
});
back.on('in', function(at){
chain.on('in', at);
});
return chain = gun.chain();
}
function put(gun, at, opt){ var err, id;
function put(at){ var err, id, opt;
(opt = at.opt || {}).prefix = opt.prefix || 'gun/';
Gun.graph.is(at.put, function(node, soul){
try{store.setItem(opt.prefix + soul, Gun.text.ify(node));
}catch(e){ err = e }
});
if(err){ explode }
Gun.on.ack(at['#'], {ok: 1});
Gun.on.ack(at, {ok: 1}); // TODO: Reliability! Are we sure we want to have localStorage ack?
}
function get(gun, at, opt){
var lex = at.get, soul, data;
function get(at){
var gun = at.gun, lex = at.get, soul, data, opt;
(opt = at.opt || {}).prefix = opt.prefix || 'gun/';
if(!lex || !(soul = lex[Gun._.soul])){ return }
data = Gun.obj.ify(store.getItem(opt.prefix + soul) || null);
if(!data){ return } // localStorage isn't trustworthy to say "not found".
gun.on('in', {put: Gun.graph.node(data)});
gun.Back(-1).on('in', {put: Gun.graph.node(data)});
}
var create = Gun.create;
function local(o){
return new create(o).local();
}
Gun.create = local;
Gun.on('put', put);
Gun.on('get', get);
})(module, './src/localStorage');
;module(function(module){
Gun.on('get', function(at){
var peers = at.gun.Back('opt.peers');
if(!peers || Gun.obj.empty(peers)){
Gun.log.once('peers', "Warning! You have no peers to connect to!");
console.debug(8, 'wsp not');
Gun.on.ack(at, {});
return;
}
});
})(module, './src/WebSocket');
return;
function get(err, data, at){
if(!data && !Gun.obj.empty(at.opt.peers)){ return } // let the peers handle no data.
@ -1621,6 +1769,7 @@
Tab.store.get((opt.prefix || '') + lex.soul, get, at);
});
}());
return;
Gun.on('put', function(at){
var opt = at.opt, graph = at.graph, gun = at.gun;

View File

@ -840,6 +840,44 @@ describe('Gun', function(){
//console.log("node/soul", n,s);
});
});
it('graph ify', function(done){
function map(v,f,n){
done.m = true;
}
var graph = Gun.graph.ify({
_: {'#': 'yay'},
a: 1
}, map);
expect(graph).to.eql({
yay: {
_: {'#': 'yay'},
a: 1
}
});
expect(done.m).to.be.ok();
var graph = Gun.graph.ify({
_: {'#': 'yay', '>': {a: 9}},
a: 1
}, map);
expect(graph).to.eql({
yay: {
_: {'#': 'yay', '>': {a: 9}},
a: 1
}
});
var map = Gun.state.map(map, 9);
var graph = Gun.graph.ify({
_: {'#': 'yay', '>': {a: 1}},
a: 1
}, map);
expect(graph).to.eql({
yay: {
_: {'#': 'yay', '>': {a: 9}},
a: 1
}
});
done();
});
});
});
describe('ify', function(){
@ -998,7 +1036,6 @@ describe('Gun', function(){
expect(gun.__.graph['asdf']).to.not.be.ok();
var ctx = Gun.HAM.graph(gun, prime);
console.log("????", ctx);
expect(ctx).to.not.be.ok();
});return;
@ -1614,18 +1651,14 @@ describe('Gun', function(){
}
it('get node put node merge', function(done){
console.debug.i = 1;console.log('-------------------------');
gun.get('hello/key', function(err, node){
if(done.soul){ return }
console.log(4, "get", err, node);
expect(err).to.not.be.ok();
expect(node.hello).to.be('key');
done.soul = Gun.node.soul(node);
}).put({hi: 'you'}, function(err, ok){
console.debug(12, "pat", err, ok);
expect(err).to.not.be.ok();
var keynode = gun.Back(-1)._.graph[done.soul], soul;
console.log("OH NO", done.soul, keynode)
expect(keynode.hi).to.not.be.ok();
var c = soulnode(gun, keynode), soul = c[0];
expect(c.length).to.be(1);
@ -1633,14 +1666,13 @@ describe('Gun', function(){
expect(node.hello).to.be('key');
expect(node.hi).to.be('you');
}).on(function(node){
console.log("******************", node);
if(done.c){ return }
//expect(done.soul).to.be(Gun.is.node.soul(node)); // TODO: DISCUSSION! This has changed?
expect(node.hi).to.be('you');
expect(node.hello).to.be('key');
done(); done.c = 1;
});
});return;
});
it('get null put node never', function(done){ // TODO: GET returns nothing, and then doing a PUT?
gun.get(null, function(err, ok){
@ -1663,6 +1695,7 @@ describe('Gun', function(){
done.err = err;
expect(err).to.not.be.ok();
expect(data).to.not.be.ok();
console.debug(10, '******', err, data);
}).put({testing: 'stuff'}, function(err, ok){
done.flag = true;
});
@ -1680,14 +1713,14 @@ describe('Gun', function(){
expect(err).to.not.be.ok();
expect(node.hello).to.be('key');
expect(node.hi).to.be('you');
done.soul = Gun.is.node.soul(node);
done.soul = Gun.node.soul(node);
}).put({hi: 'overwritten'}, function(err, ok){
if(done.c){ return }
expect(err).to.not.be.ok();
var keynode = gun.__.graph[done.soul], soul;
var keynode = gun.Back(-1)._.graph[done.soul], soul;
var c = soulnode(gun, keynode), soul = c[0];
expect(c.length).to.be(1);
var node = gun.__.graph[soul];
var node = gun.Back(-1)._.graph[soul];
expect(node.hello).to.be('key');
expect(node.hi).to.be('overwritten');
done.w = 1; if(done.r){ done(); done.c = 1 };
@ -1699,19 +1732,20 @@ describe('Gun', function(){
done.r = 1; if(done.w){ done(); done.c = 1 };
});
});
it('get key path put', function(done){
var gun = Gun().put({foo:'lol', extra: 'yes'}).key('key/path/put');
var data = gun.get('key/path/put');
data.path('foo').put('epic');
data.val(function(val, field){
expect(val.foo).to.be('epic');
expect(Gun.is.node.soul(val)).to.be('key/path/put');
expect(Gun.node.soul(val)).to.be('key/path/put');
done();
});
});
it('put node path', function(done){
var gun = Gun();
gun.put({hello: 'world'}).path('hello', function(err, val, field){
if(done.end){ return } // it is okay for path's callback to be called multiple times.
expect(err).to.not.be.ok();
@ -1741,6 +1775,7 @@ describe('Gun', function(){
done(); done.end = true;
});
});
it('get node path', function(done){
gun.get('hello/key').path('hi', function(err, val, field){
if(done.end){ return } // it is okay for path's callback to be called multiple times.
@ -1767,14 +1802,14 @@ describe('Gun', function(){
expect(err).to.not.be.ok();
if(done.soul){ return }
expect(node.hi).to.be('overwritten');
done.soul = Gun.is.node.soul(node);
done.soul = Gun.node.soul(node);
}).path('hi').put('again', function(err, ok){
if(done.c){ return }
expect(err).to.not.be.ok();
var keynode = gun.__.graph[done.soul], soul;
var keynode = gun.Back(-1)._.graph[done.soul], soul;
var c = soulnode(gun, keynode), soul = c[0];
expect(c.length).to.be(1);
var node = gun.__.graph[done.sub = soul];
var node = gun.Back(-1)._.graph[done.sub = soul];
expect(node.hello).to.be('key');
expect(node.hi).to.be('again');
done.w = 1; if(done.r){ done(); done.c = 1 };
@ -1787,37 +1822,40 @@ describe('Gun', function(){
});
it('get node path put object', function(done){
gun.get('hello/key', function(err, node){
console.debug.i=1;console.log("----------------------");
var foo = gun.get('hello/key', function(err, node){
if(done.soul){ return }
expect(err).to.not.be.ok();
expect(node.hi).to.be('again');
expect(node.hello).to.be('key');
done.soul = Gun.is.node.soul(node);
done.soul = Gun.node.soul(node);
console.debug(3, '****', err, node, done.soul);
}).path('hi').put({yay: "value"}, function(err, ok){
if(done.c){ return }
expect(err).to.not.be.ok();
var keynode = gun.__.graph[done.soul], soul;
var keynode = gun.Back(-1)._.graph[done.soul], soul;
var c = soulnode(gun, keynode), soul = c[0];
expect(c.length).to.be(1);
var root = gun.__.graph[soul];
var root = gun.Back(-1)._.graph[soul];
expect(root.hello).to.be('key');
expect(root.yay).to.not.be.ok();
expect(Gun.is.rel(root.hi)).to.be.ok();
expect(Gun.is.rel(root.hi)).to.not.be(soul);
var node = gun.__.graph[Gun.is.rel(root.hi)];
expect(Gun.val.rel.is(root.hi)).to.be.ok();
expect(Gun.val.rel.is(root.hi)).to.not.be(soul);
var node = gun.Back(-1)._.graph[Gun.val.rel.is(root.hi)];
expect(node.yay).to.be('value');
if(done.sub){ expect(done.sub).to.be(Gun.is.rel(root.hi)) }
else { done.sub = Gun.is.rel(root.hi) }
if(done.sub){ expect(done.sub).to.be(Gun.val.rel.is(root.hi)) }
else { done.sub = Gun.val.rel.is(root.hi) }
done.w = 1; if(done.r){ done(); done.c = 1 };
}).on(function(node, field){
console.log("******************", field, node);return;
if(done.c){ return }
expect(field).to.be('hi');
expect(node.yay).to.be('value');
if(done.sub){ expect(done.sub).to.be(Gun.is.node.soul(node)) }
else { done.sub = Gun.is.node.soul(node) }
if(done.sub){ expect(done.sub).to.be(Gun.node.soul(node)) }
else { done.sub = Gun.node.soul(node) }
done.r = 1; if(done.w){ done(); done.c = 1 };
});
});
});return;
it('get path wire', function(done){
var gun = Gun();