mirror of
https://github.com/amark/gun.git
synced 2025-03-30 15:08:33 +00:00
ready to be rolling soon
This commit is contained in:
parent
e89c19cdd8
commit
3e6fc92949
@ -10,7 +10,7 @@
|
||||
}
|
||||
</style>
|
||||
<a href="todo/index.html"><iframe src="todo/index.html"></iframe></a>
|
||||
<a href="angular/index.html"><iframe src="angular/index.html"></iframe></a>
|
||||
<a href="json/index.html"><iframe src="json/index.html"></iframe></a>
|
||||
<script src="../../gun.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
75
examples/json/index.html
Normal file
75
examples/json/index.html
Normal file
@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="../../gun.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h3>Admin JSON Editor</h3>
|
||||
This is a live view of your data, you can edit it in realtime or add new field/values.
|
||||
<ul id="data">
|
||||
</ul>
|
||||
<li id="model">
|
||||
<b>field</b>:
|
||||
<span contenteditable="true">val</span>
|
||||
</li>
|
||||
<ul><li>
|
||||
<form id="form" onsubmit="add()">
|
||||
<label>
|
||||
<input id="field" placeholder="field">
|
||||
<button type="submit">add</button>
|
||||
</label>
|
||||
</form>
|
||||
</li></ul>
|
||||
<script>
|
||||
var ref = Gun(location.origin + '/gun').get('example/json/data');
|
||||
ref.not(function(){
|
||||
return this.put({hello: "world!"}).key('example/json/data');
|
||||
}).on(function(data){
|
||||
for(var field in data){
|
||||
if(field === '_'){ continue } // skip meta data!
|
||||
var val = (data[field = field || ''] || '').toString(), id = field.replace(/[^A-z]/ig, ''), elem; // make data safe.
|
||||
(elem = $('#' + id) || $('#data').appendChild($('#model').cloneNode(true))).id = id; // reuse or make element, set id.
|
||||
$('b', elem).innerHTML = field.replace(/\</ig, '<'); // escape and display field
|
||||
$('span', elem).innerHTML = val.replace(/\</ig, '<'); // escape and display value
|
||||
}
|
||||
});
|
||||
|
||||
var $ = function(s, elem){ return (elem || document).querySelector(s) } // make native look like jQuery.
|
||||
document.onkeyup = function(e){
|
||||
if(!e || !e.target){ return } // ignore if no element!
|
||||
if(!e.target.attributes.contenteditable){ return } // ignore if element content isn't editable!
|
||||
ref.path((e.target.previousElementSibling.innerHTML||'').toString().replace(/\</ig, '<')) // grab the label, which is in the previous element.
|
||||
.put( (e.target.innerHTML||'').toString().replace(/\</ig, '<') ); // insert the value of the text in the current element.
|
||||
}
|
||||
$('#form').onsubmit = function add(e){
|
||||
return ref.path($('#field').value || '').put("value"), false; // add a new field, and cancel the form submit.
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
html, body {
|
||||
font-family: Verdana, Geneva, sans-serif;
|
||||
}
|
||||
a, button {
|
||||
border: none;
|
||||
color: skyblue;
|
||||
background: transparent;
|
||||
text-decoration: none;
|
||||
cursor: poiner;
|
||||
}
|
||||
ul, li {
|
||||
list-style-type: none;
|
||||
}
|
||||
ul:hover, li:hover {
|
||||
list-style-type: inherit;
|
||||
}
|
||||
input {
|
||||
border: none;
|
||||
border-bottom: dashed 1px gainsboro;
|
||||
}
|
||||
.none, #model {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
@ -9,10 +9,11 @@
|
||||
<script>
|
||||
// by Forrest Tait! Edited by Mark Nadal.
|
||||
function ready(){
|
||||
Gun.log.verbose = true;
|
||||
var $ = document.querySelector.bind(document);
|
||||
var gun = Gun(location.origin + '/gun').get('example/todo/data');
|
||||
gun.on(function renderToDo(val){
|
||||
gun.not(function(){
|
||||
return this.put({hello: "world!"}).key('example/todo/data');
|
||||
}).on(function renderToDo(val){
|
||||
var todoHTML = '';
|
||||
for(key in val) {
|
||||
if(!val[key] || key == '_') continue;
|
||||
|
250
gun.js
250
gun.js
@ -55,7 +55,7 @@
|
||||
return !Gun.obj.map(node, function(value, field){ // need to invert this, because the way we check for this is via a negation.
|
||||
if(field == Gun._.meta){ return } // skip this.
|
||||
if(!Gun.is.value(value)){ return true } // it is true that this is an invalid node.
|
||||
if(cb){ cb(value, field) }
|
||||
if(cb){ cb(value, field, node._) }
|
||||
});
|
||||
}
|
||||
return false;
|
||||
@ -70,8 +70,8 @@
|
||||
}) && exist;
|
||||
}
|
||||
// Gun.ify // the serializer is too long for right here, it has been relocated towards the bottom.
|
||||
Gun.union = function(gun, prime){
|
||||
var ctx = {};
|
||||
Gun.union = function(gun, prime, cb){
|
||||
var ctx = {count: 0, cb: function(){ cb = cb? cb() && null : null }};
|
||||
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!") } }
|
||||
@ -80,16 +80,22 @@
|
||||
ctx.tmp[ctx.soul] = prime;
|
||||
prime = ctx.tmp;
|
||||
}
|
||||
Gun.is.graph(prime, null, function(val, field, meta){
|
||||
if(!meta || !(meta = meta[Gun._.HAM]) || !Gun.num.is(meta[field])){
|
||||
return ctx.err = {err: Gun.log("No state on " + field + "!") }
|
||||
}
|
||||
});
|
||||
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
|
||||
if(!vertex){ // disjoint // TODO: Maybe not correct? BUG, probably.
|
||||
gun.__.on(soul).emit(graph[soul] = node);
|
||||
return;
|
||||
}
|
||||
ctx.count += 1;
|
||||
Gun.HAM(vertex, node, function(){}, function(vertex, field, value){
|
||||
if(!vertex){ return }
|
||||
var change = {};
|
||||
@ -101,27 +107,27 @@
|
||||
//context.nodes[change._[Gun._.soul]] = change;
|
||||
//context('change').fire(change);
|
||||
gun.__.on(Gun.is.soul.on(change)).emit(change);
|
||||
}, function(){
|
||||
|
||||
}, function(){})(function(){
|
||||
if(!(ctx.count -= 1)){ ctx.cb() }
|
||||
});
|
||||
});
|
||||
})(ctx.graph, prime);
|
||||
if(!ctx.count){ ctx.cb() }
|
||||
return ctx;
|
||||
}
|
||||
Gun.HAM = function(vertex, delta, lower, each, upper){
|
||||
Gun.HAM = function(vertex, delta, lower, now, upper){
|
||||
upper.max = -Infinity;
|
||||
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 ctx = {incoming: {}, current: {}}, state;
|
||||
ctx.drift = Gun.time.is();
|
||||
ctx.incoming.value = Gun.is.soul(incoming) || incoming;
|
||||
ctx.current.value = Gun.is.soul(vertex[field]) || vertex[field]
|
||||
ctx.incoming.state = ((delta._||{})[Gun._.HAM]||{})[field] || 0;
|
||||
ctx.current.state = ((vertex._||{})[Gun._.HAM]||{})[field] || 0;
|
||||
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;
|
||||
ctx.current.state = Gun.num.is(ctx.tmp = ((vertex._||{})[Gun._.HAM]||{})[field])? ctx.tmp : -Infinity;
|
||||
upper.max = ctx.incoming.state > upper.max? ctx.incoming.state : upper.max;
|
||||
state = HAM(ctx.drift, ctx.incoming.state, ctx.current.state, ctx.incoming.value, ctx.current.value);
|
||||
//console.log("HAM:", ctx);
|
||||
//root.console.log("HAM:", ctx);
|
||||
if(state.err){
|
||||
root.console.log(".!HYPOTHETICAL AMNESIA MACHINE ERR!.", state.err); // this error should never happen.
|
||||
return;
|
||||
@ -131,13 +137,15 @@
|
||||
return;
|
||||
}
|
||||
if(state.incoming){
|
||||
each.call(state, vertex, field, incoming);
|
||||
now.call(state, vertex, field, incoming);
|
||||
return;
|
||||
}
|
||||
if(state.amnesiaQuarantine){
|
||||
upper.wait = true;
|
||||
upper.call(state, vertex, field, incoming); // signals that there are still future modifications.
|
||||
Gun.schedule(ctx.incoming.state, function(){
|
||||
update(incoming, field);
|
||||
upper.call(state, vertex, field, incoming);
|
||||
if(ctx.incoming.state === upper.max){ upper.last() }
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -175,15 +183,10 @@
|
||||
}
|
||||
return {err: "you have not properly handled recursion through your data or filtered it as JSON"};
|
||||
}
|
||||
}
|
||||
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);
|
||||
});
|
||||
return function(fn){
|
||||
upper.last = fn || function(){};
|
||||
if(!upper.wait){ upper.last() }
|
||||
}
|
||||
}
|
||||
Gun.roulette = function(l, c){
|
||||
var gun = Gun.is(this)? this : {};
|
||||
@ -221,13 +224,31 @@
|
||||
});
|
||||
if(!stun){ Gun.on('opt').emit(gun, opt) }
|
||||
return gun;
|
||||
}
|
||||
}
|
||||
Gun.chain.chain = function(from){
|
||||
var gun = Gun(null);
|
||||
from = from || this;
|
||||
gun.back = from;
|
||||
gun.__ = from.__;
|
||||
gun._ = {on: Gun.on.create() };
|
||||
gun._ = {on: Gun.on.create()};
|
||||
gun._.status = function(e){
|
||||
var proxy = function(chain, cb){
|
||||
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);
|
||||
});
|
||||
}
|
||||
proxy.event = function(cb){ return proxy('event', cb) };
|
||||
proxy.once = function(cb){ return proxy('once', cb) };
|
||||
proxy.emit = function(){
|
||||
var args = arguments;
|
||||
setImmediate(function(me){ (me = gun._.on(e)).emit.apply(me, args) })
|
||||
};
|
||||
return proxy;
|
||||
}
|
||||
return gun;
|
||||
}
|
||||
Chain.get = function(key, cb, opt){ // get opens up a reference to a node and loads it.
|
||||
@ -238,46 +259,46 @@
|
||||
cb = cb || function(){};
|
||||
opt = opt || {};
|
||||
if(ctx.soul){
|
||||
Gun.fns.async(function(){ open(ctx.soul) });
|
||||
gun._.status('soul').emit({soul: ctx.soul});
|
||||
if(ctx.node = gun.__.graph[ctx.soul]){ // in memory
|
||||
cb.call(gun, null, Gun.obj.copy(ctx.node));
|
||||
gun._.status('node').emit({soul: ctx.soul});
|
||||
} else { load(key) } // not in memory
|
||||
} else
|
||||
if(ctx.key){
|
||||
|
||||
(function foo(){ // TODO: JANKY! UGLY!!!!
|
||||
(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){
|
||||
Gun.fns.async(foo);
|
||||
setTimeout(foo,0);
|
||||
} else {
|
||||
cb.call(gun, null, Gun.obj.copy(ctx.node));
|
||||
Gun.fns.async(function(){ open(Gun.is.soul.on(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
|
||||
})();
|
||||
|
||||
} 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(!data){ return cb.call(gun, null, null), gun._.status('null').emit() }
|
||||
if(ctx.soul = Gun.is.soul.on(data)){
|
||||
open(ctx.soul);
|
||||
gun._.status('soul').emit({soul: 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);
|
||||
gun._.status('node').emit({soul: ctx.soul});
|
||||
}, 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.
|
||||
gun._.status('null').emit();
|
||||
}
|
||||
}
|
||||
return gun;
|
||||
@ -288,14 +309,12 @@
|
||||
cb = cb || function(){};
|
||||
opt = opt || {};
|
||||
gun.__.keys[key] = true;
|
||||
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;
|
||||
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){
|
||||
ctx.hook(key, $.soul, function(err, data){
|
||||
return cb.call(gun, err, data);
|
||||
}, opt);
|
||||
} else {
|
||||
@ -307,6 +326,7 @@
|
||||
}
|
||||
Chain.all = function(key, cb){
|
||||
var gun = this.chain();
|
||||
return gun; // TODO: BUG! We need to create all!
|
||||
cb = cb || function(){};
|
||||
gun.shot.next(function(next){
|
||||
Gun.obj.map(gun.__.keys, function(node, key){ // TODO: BUG!! Need to handle souls too!
|
||||
@ -349,21 +369,16 @@
|
||||
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
|
||||
// TODO: Hmmm once also? figure it out later.
|
||||
gun.back._.status('node').event(function trace($){ // TODO: Check for field as well and merge?
|
||||
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) }
|
||||
trace(Gun.is.soul.on(data));
|
||||
trace({soul: Gun.is.soul.on(data)});
|
||||
});
|
||||
} else {
|
||||
cb.call(gun, null);
|
||||
@ -371,14 +386,20 @@
|
||||
} 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.
|
||||
gun._.on('soul').emit({soul: $.soul, field: field}); // if .put is after, makes sense. If anything else, makes sense to wait.
|
||||
gun._.on('node').emit({soul: $.soul, field: field});
|
||||
} else
|
||||
if(Gun.is.soul(val = node[field])){
|
||||
gun.get(val, cb);
|
||||
gun._.on('soul').emit(Gun.is.soul(val), null, soul, field);
|
||||
gun.get(val, function(err, data){
|
||||
cb.call(gun, err, data);
|
||||
if(err || !data){ return }
|
||||
gun._.status('node').emit({soul: Gun.is.soul(val)});
|
||||
});
|
||||
gun._.on('soul').emit({soul: Gun.is.soul(val), field: null, from: $.soul, at: field});
|
||||
} else {
|
||||
cb.call(gun, null, val, field);
|
||||
gun._.on('soul').emit(soul, field);
|
||||
gun._.on('soul').emit({soul: $.soul, field: field});
|
||||
gun._.on('node').emit({soul: $.soul, field: field});
|
||||
}
|
||||
});
|
||||
|
||||
@ -386,14 +407,11 @@
|
||||
}
|
||||
Chain.val = function(cb){
|
||||
var gun = this, ctx = {};
|
||||
cb = cb || function(){};
|
||||
cb = cb || root.console.log.bind(root.console);
|
||||
|
||||
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));
|
||||
});
|
||||
gun._.status('node').event(function($){ // TODO: once per soul on graph. (?)
|
||||
var node = gun.__.graph[$.soul];
|
||||
cb.call(gun, $.field? node[$.field] : Gun.obj.copy(node)); // TODO: at terminating
|
||||
});
|
||||
|
||||
return gun;
|
||||
@ -403,10 +421,12 @@
|
||||
var gun = this, ctx = {};
|
||||
cb = cb || function(){};
|
||||
|
||||
Gun.when(gun, function(soul, field){
|
||||
gun.__.on(soul).event(function(delta){
|
||||
var node = gun.__.graph[soul];
|
||||
cb.call(gun, Gun.obj.copy(node), field);
|
||||
// TODO: below is also probably going to be on node.
|
||||
gun.val(cb)._.status('soul').event(function($){ // TODO: once per soul on graph. (?)
|
||||
// TODO: Don't use val :(, but trigger callback now as well.
|
||||
gun.__.on($.soul).event(function(delta){
|
||||
var node = gun.__.graph[$.soul];
|
||||
cb.call(gun, Gun.obj.copy(node), $.field);
|
||||
});
|
||||
});
|
||||
|
||||
@ -424,40 +444,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.
|
||||
*/
|
||||
Chain.put = function(val, cb, opt){ // handle case where val is a gun context!
|
||||
var gun = this.chain(), drift = Gun.time.is(), flag;
|
||||
var gun = this.chain(), drift = Gun.time.is();
|
||||
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._.status('soul').emit({soul: Gun.is.soul.on(val) || Gun.roulette.call(gun), empty: true});
|
||||
}
|
||||
Gun.when(gun.back, function(soul, field, from, at){
|
||||
//console.log("chain.put", field, val, "on", soul, 'or', from, at);
|
||||
var ctx = {}, obj = val;
|
||||
gun.back._.status('soul').event(function($){ // TODO: maybe once per soul?
|
||||
var ctx = {}, obj = val, $ = Gun.obj.copy($);
|
||||
console.log("chain.put", val, $);
|
||||
if(Gun.is.value(obj)){
|
||||
if(from && at){
|
||||
soul = from;
|
||||
field = at;
|
||||
if($.from && $.at){
|
||||
$.soul = $.from;
|
||||
$.field = $.at;
|
||||
} // no else!
|
||||
if(!field){
|
||||
if(!$.field){
|
||||
return cb.call(gun, {err: Gun.log("No field exists for " + (typeof obj) + "!")});
|
||||
} else
|
||||
if(gun.__.graph[soul]){
|
||||
if(gun.__.graph[$.soul]){
|
||||
ctx.tmp = {};
|
||||
ctx.tmp[ctx.field = field] = obj;
|
||||
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){
|
||||
if($.field && !ctx.field){
|
||||
ctx.tmp = {};
|
||||
ctx.tmp[ctx.field = field] = obj;
|
||||
ctx.tmp[ctx.field = $.field] = obj;
|
||||
obj = ctx.tmp;
|
||||
}
|
||||
Gun.ify(obj, function(env, cb){
|
||||
@ -468,10 +485,10 @@
|
||||
}
|
||||
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] = $.soul] = at.node;
|
||||
cb(at, $.soul);
|
||||
} else {
|
||||
flag? path() : gun.back.path(at.path.join('.'), path); // TODO: clean this up.
|
||||
$.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;
|
||||
@ -485,11 +502,12 @@
|
||||
if(!at.field){ return }
|
||||
at.node._[Gun._.HAM][at.field] = drift;
|
||||
})(function(err, ify){
|
||||
console.log("chain.put PUT", ify.graph);
|
||||
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($.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});
|
||||
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) }
|
||||
@ -524,16 +542,17 @@
|
||||
var ahead = gun.chain(); return ahead;
|
||||
return gun;
|
||||
}
|
||||
Chain.not = function(not){
|
||||
var gun = this;
|
||||
not = not || function(){};
|
||||
gun.shot.next(function(next){
|
||||
if(gun._.node){ // if it does indeed exist
|
||||
return next(); // yet fire off the chain
|
||||
}
|
||||
not.call(gun); // call not
|
||||
next(); // fire off the chain
|
||||
Chain.not = function(cb){
|
||||
var gun = this, ctx = {};
|
||||
cb = cb || function(){};
|
||||
|
||||
gun._.status('null').once(function(){
|
||||
var chain = gun.chain(), next = cb.call(chain);
|
||||
next._.status('soul').event(function($){ gun._.on('soul').emit($) });
|
||||
next._.status('node').event(function($){ gun._.on('node').emit($) });
|
||||
chain._.on('soul').emit({soul: Gun.roulette.call(chain), empty: true});
|
||||
});
|
||||
|
||||
return gun;
|
||||
}
|
||||
Chain.err = function(dud){ // WARNING: dud was depreciated.
|
||||
@ -544,10 +563,6 @@
|
||||
;(function(Util){
|
||||
Util.fns = {};
|
||||
Util.fns.is = function(fn){ return (fn instanceof Function)? true : false }
|
||||
Util.fns.async = (function(){
|
||||
var setImmediate = setImmediate || function(cb){return setTimeout(cb,0)};
|
||||
return setImmediate;
|
||||
})();
|
||||
Util.fns.sum = function(done){ // combine with Util.obj.map for some easy parallel async operations!
|
||||
var context = {task: {}, data: {}};
|
||||
context.end = function(e,v){ return done(e,v), done = function(){} };
|
||||
@ -748,17 +763,10 @@
|
||||
function ify(data, cb, opt){
|
||||
opt = opt || {};
|
||||
cb = cb || function(env, cb){ cb(env.at, Gun.roulette()) };
|
||||
var ctx = {}, end = function(fn){
|
||||
Gun.fns.async(function wait(){ // TODO: JANKY! 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.
|
||||
}
|
||||
});
|
||||
}
|
||||
var end = function(fn){
|
||||
ctx.end = fn || function(){};
|
||||
if(ctx.err){ return ctx.end(ctx.err, ctx), ctx.end = function(){} }
|
||||
}, ctx = {};
|
||||
if(!data){ return ctx.err = Gun.log('Serializer does not have correct parameters.'), end }
|
||||
ctx.at = {};
|
||||
ctx.root = {};
|
||||
@ -811,6 +819,7 @@
|
||||
}
|
||||
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;
|
||||
@ -818,6 +827,11 @@
|
||||
});
|
||||
});
|
||||
}
|
||||
function unique(ctx){
|
||||
if(ctx.err || !Gun.list.map(ctx.seen, function(at){
|
||||
if(!at.soul){ return true }
|
||||
})){ return ctx.end(ctx.err, ctx), ctx.end = function(){} }
|
||||
}
|
||||
function seen(ctx, at){
|
||||
return Gun.list.map(ctx.seen, function(has){
|
||||
if(at.obj === has.obj){ return has }
|
||||
@ -830,6 +844,7 @@
|
||||
} 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.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}};
|
||||
@ -917,6 +932,7 @@
|
||||
});
|
||||
Gun.obj.map(gun.__.opt.peers, function(peer, url){
|
||||
request(url, nodes, function(err, reply){
|
||||
console.log("PUT success?", err, reply);
|
||||
if(err || !reply || (err = reply.body && reply.body.err)){
|
||||
return cb({err: Gun.log(err || "Error: Put failed on " + url) });
|
||||
} else {
|
||||
@ -925,7 +941,7 @@
|
||||
}, {headers: tab.headers});
|
||||
cb.peers = true;
|
||||
}); tab.peers(cb);
|
||||
Gun.obj.map(nodes, function(node, soul){
|
||||
Gun.obj.map(nodes, function(node, soul){ // TODO: BUG? is this really necessary?
|
||||
gun.__.on(soul).emit(node);
|
||||
});
|
||||
}
|
||||
@ -938,7 +954,7 @@
|
||||
request.createServer(function(req, res){
|
||||
if(!req.body){ return }
|
||||
if(Gun.is.node(req.body) || Gun.is.graph(req.body)){
|
||||
console.log("client server received request", req);
|
||||
Gun.log("client server received request", req);
|
||||
Gun.union(gun, req.body); // TODO: BUG? Interesting, this won't update localStorage because .put isn't called?
|
||||
}
|
||||
});
|
||||
|
@ -380,6 +380,11 @@ html, body {
|
||||
</div> <!-- END FOLD -->
|
||||
<a href="https://github.com/amark/gun" target="_blank"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/365986a132ccd6a44c23a9169022c0b5c890c387/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f7265645f6161303030302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png"></a>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.heap=window.heap||[],heap.load=function(t,e){window.heap.appid=t,window.heap.config=e;var a=document.createElement("script");a.type="text/javascript",a.async=!0,a.src=("https:"===document.location.protocol?"https:":"http:")+"//cdn.heapanalytics.com/js/heap-"+t+".js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(a,n);for(var o=function(t){return function(){heap.push([t].concat(Array.prototype.slice.call(arguments,0)))}},p=["clearEventProperties","identify","setEventProperties","track","unsetEventProperty"],c=0;c<p.length;c++)heap[p[c]]=o(p[c])};
|
||||
heap.load("2174172773");
|
||||
</script>
|
||||
|
||||
<!-- BEGIN TUTORIAL -->
|
||||
<div class="tutorial">
|
||||
<script src="https://gunjs.herokuapp.com/gun.js"></script>
|
||||
|
20
lib/wsp.js
20
lib/wsp.js
@ -127,7 +127,25 @@
|
||||
var reply = {headers: {'Content-Type': tran.json}};
|
||||
if(!req.body){ return cb({headers: reply.headers, body: {err: "No body"}}) }
|
||||
if(tran.put.key(req, cb)){ return }
|
||||
console.log("tran.put", req.body);
|
||||
|
||||
// some NEW code that should get revised.
|
||||
if(Gun.is.node(req.body) || Gun.is.graph(req.body)){
|
||||
console.log("tran.put", req.body);
|
||||
if(req.err = Gun.union(gun, req.body, function(err, ctx){ // TODO: BUG? Probably should give me ctx.graph
|
||||
if(err){ return cb({headers: reply.headers, body: {err: err || "Union failed."}}) }
|
||||
var ctx = ctx || {}; ctx.graph = {};
|
||||
Gun.is.graph(req.body, function(node, soul){
|
||||
ctx.graph[soul] = gun.__.graph[soul]; // TODO: BUG? Probably should be delta fields
|
||||
})
|
||||
gun.__.opt.hooks.put(ctx.graph, function(err, ok){
|
||||
if(err){ return cb({headers: reply.headers, body: {err: err || "Failed."}}) }
|
||||
cb({headers: reply.headers, body: {ok: ok || "Persisted."}});
|
||||
});
|
||||
}).err){ cb({headers: reply.headers, body: {err: req.err || "Union failed."}}) }
|
||||
}
|
||||
gun.server.on('network').emit(req);
|
||||
|
||||
return;
|
||||
//return;
|
||||
// saving
|
||||
Gun.obj.map(req.body, function(node, soul){ // iterate over every node
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
263
test/common.js
263
test/common.js
@ -356,42 +356,23 @@ describe('Gun', function(){
|
||||
});
|
||||
});
|
||||
|
||||
return; // TODO: COME BACK HERE?
|
||||
it('circular reference', function(done){
|
||||
data = {};
|
||||
data.a = {x: 1, y: 2, z: 3}
|
||||
data.b = {m: 'n', o: 'p', q: 'r', s: 't'};
|
||||
data.a.kid = data.b;
|
||||
data.b.parent = data.a;
|
||||
it('extraneous', function(done){
|
||||
var data = {_: {'#': 'shhh', meta: {yay: 1}}, sneak: true};
|
||||
Gun.ify(data)(function(err, ctx){
|
||||
console.log("circ ref", err, ctx, 'now see:');
|
||||
ctx.seen.forEach(function(val){ console.log(val.node) });
|
||||
expect(test.err).to.not.be.ok();
|
||||
expect(err).to.not.be.ok(); // extraneous metadata needs to be stored, but it can't be used for data.
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
data = {_: {'#': 'shhh', meta: {yay: 1}}, sneak: true};
|
||||
test = Gun.ify(data);
|
||||
expect(test.err).to.not.be.ok(); // metadata needs to be stored, but it can't be used for data.
|
||||
|
||||
return; // TODO! Fix GUN to handle this!
|
||||
data = {};
|
||||
data.sneak = false;
|
||||
data.both = {inside: 'meta data'};
|
||||
data._ = {'#': 'shhh', data: {yay: 1}, spin: data.both};
|
||||
test = Gun.ify(data);
|
||||
expect(test.err.meta).to.be.ok(); // TODO: Fail: this passes, somehow? Fix ify code!
|
||||
|
||||
it('union', function(){
|
||||
var graph, prime;
|
||||
|
||||
graph = Gun.ify({a: false, b: true, c: 0, d: 1, e: '', f: 'g', h: null}).nodes;
|
||||
prime = Gun.ify({h: 9, i: 'foo', j: 'k', l: 'bar', m: 'Mark', n: 'Nadal'}).nodes;
|
||||
|
||||
Gun.union(graph, prime); // TODO: BUG! Where is the expect???
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('Event Promise Back In Time', function(){ return;
|
||||
/*
|
||||
var ref = gun.put({field: 'value'}).key('field/value').get('field/value', function(){
|
||||
@ -498,10 +479,207 @@ describe('Gun', function(){
|
||||
});
|
||||
});
|
||||
|
||||
describe('API', function(){
|
||||
describe('Union', function(){
|
||||
var gun = Gun();
|
||||
|
||||
it('fail', function(){
|
||||
var prime = {
|
||||
'asdf': {
|
||||
_: {'#': 'asdf', '>':{
|
||||
a: 'cheating'
|
||||
}},
|
||||
a: 0
|
||||
}
|
||||
}
|
||||
|
||||
//(typeof window === 'undefined') && require('../lib/file');
|
||||
var gun = Gun(); //Gun({file: 'data.json'});
|
||||
expect(gun.__.graph['asdf']).to.not.be.ok();
|
||||
var ctx = Gun.union(gun, prime);
|
||||
expect(ctx.err).to.be.ok();
|
||||
});
|
||||
|
||||
it('basic', function(done){
|
||||
var prime = {
|
||||
'asdf': {
|
||||
_: {'#': 'asdf', '>':{
|
||||
a: Date.now()
|
||||
}},
|
||||
a: 0
|
||||
}
|
||||
}
|
||||
|
||||
expect(gun.__.graph['asdf']).to.not.be.ok();
|
||||
var ctx = Gun.union(gun, prime, function(){
|
||||
expect(gun.__.graph['asdf'].a).to.be(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('disjoint', function(done){
|
||||
var prime = {
|
||||
'asdf': {
|
||||
_: {'#': 'asdf', '>':{
|
||||
b: Date.now()
|
||||
}},
|
||||
b: 'c'
|
||||
}
|
||||
}
|
||||
|
||||
expect(gun.__.graph['asdf'].a).to.be(0);
|
||||
expect(gun.__.graph['asdf'].b).to.not.be.ok();
|
||||
var ctx = Gun.union(gun, prime, function(){
|
||||
expect(gun.__.graph['asdf'].a).to.be(0);
|
||||
expect(gun.__.graph['asdf'].b).to.be('c');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('mutate', function(done){
|
||||
var prime = {
|
||||
'asdf': {
|
||||
_: {'#': 'asdf', '>':{
|
||||
b: Date.now()
|
||||
}},
|
||||
b: 'd'
|
||||
}
|
||||
}
|
||||
|
||||
expect(gun.__.graph['asdf'].b).to.be('c');
|
||||
var ctx = Gun.union(gun, prime, function(){
|
||||
expect(gun.__.graph['asdf'].b).to.be('d');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('disjoint past', function(done){
|
||||
var prime = {
|
||||
'asdf': {
|
||||
_: {'#': 'asdf', '>':{
|
||||
x: 0 // beginning of time!
|
||||
}},
|
||||
x: 'hi'
|
||||
}
|
||||
}
|
||||
|
||||
expect(gun.__.graph['asdf'].x).to.not.be.ok();
|
||||
var ctx = Gun.union(gun, prime, function(){
|
||||
expect(gun.__.graph['asdf'].x).to.be('hi');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('past', function(done){
|
||||
var prime = {
|
||||
'asdf': {
|
||||
_: {'#': 'asdf', '>':{
|
||||
x: Date.now() - (60 * 1000) // above lower boundary, below now or upper boundary.
|
||||
}},
|
||||
x: 'hello'
|
||||
}
|
||||
}
|
||||
|
||||
expect(gun.__.graph['asdf'].x).to.be('hi');
|
||||
var ctx = Gun.union(gun, prime, function(){
|
||||
expect(gun.__.graph['asdf'].x).to.be('hello');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('future', function(done){
|
||||
var prime = {
|
||||
'asdf': {
|
||||
_: {'#': 'asdf', '>':{
|
||||
x: Date.now() + (100) // above now or upper boundary, aka future.
|
||||
}},
|
||||
x: 'how are you?'
|
||||
}
|
||||
}
|
||||
|
||||
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(gun.__.graph['asdf'].x).to.be('how are you?');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('disjoint future', function(done){
|
||||
var prime = {
|
||||
'asdf': {
|
||||
_: {'#': 'asdf', '>':{
|
||||
y: Date.now() + (100) // above now or upper boundary, aka future.
|
||||
}},
|
||||
y: 'goodbye'
|
||||
}
|
||||
}
|
||||
|
||||
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(gun.__.graph['asdf'].y).to.be('goodbye');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('disjoint future max', function(done){
|
||||
var prime = {
|
||||
'asdf': {
|
||||
_: {'#': 'asdf', '>':{
|
||||
y: Date.now() + (2), // above now or upper boundary, aka future.
|
||||
z: Date.now() + (100) // above now or upper boundary, aka future.
|
||||
}},
|
||||
y: 'bye',
|
||||
z: 'who'
|
||||
}
|
||||
}
|
||||
|
||||
expect(gun.__.graph['asdf'].y).to.be('goodbye');
|
||||
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(gun.__.graph['asdf'].y).to.be('bye');
|
||||
expect(gun.__.graph['asdf'].z).to.be('who');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('future max', function(done){
|
||||
var prime = {
|
||||
'asdf': {
|
||||
_: {'#': '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.
|
||||
z: Date.now() + (50) // above now or upper boundary, aka future.
|
||||
}},
|
||||
w: true,
|
||||
x: 'nothing',
|
||||
y: 'farewell',
|
||||
z: 'doctor who'
|
||||
}
|
||||
}
|
||||
|
||||
expect(gun.__.graph['asdf'].w).to.not.be.ok();
|
||||
expect(gun.__.graph['asdf'].x).to.be('how are you?');
|
||||
expect(gun.__.graph['asdf'].y).to.be('bye');
|
||||
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(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');
|
||||
expect(gun.__.graph['asdf'].z).to.be('doctor who');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('API', function(){
|
||||
var gun = Gun();
|
||||
|
||||
it('put', function(done){
|
||||
gun.put("hello", function(err){
|
||||
@ -748,7 +926,6 @@ describe('Gun', function(){
|
||||
|
||||
it('get path', function(done){
|
||||
gun.get('hello/world').path('hello').val(function(val){
|
||||
console.log("comfy stuff, pal", val);
|
||||
expect(val).to.be('world');
|
||||
done();
|
||||
});
|
||||
@ -763,7 +940,6 @@ describe('Gun', function(){
|
||||
|
||||
it('get path put', function(done){
|
||||
gun.get('hello/world').path('hello').put('World').val(function(val){
|
||||
console.log("what up dawg?", val);
|
||||
expect(val).to.be('World');
|
||||
done();
|
||||
});
|
||||
@ -799,18 +975,18 @@ describe('Gun', function(){
|
||||
});
|
||||
*/
|
||||
|
||||
it('get not kick val', function(done){ console.log("Undo this!"); return done(); // TODO // it would be cool with GUN
|
||||
it('get not kick val', function(done){
|
||||
gun.get("some/empty/thing").not(function(){ // that if you call not first
|
||||
this.put({now: 'exists'}); // you can put stuff
|
||||
return this.put({now: 'exists'}).key("some/empty/thing"); // you can put stuff
|
||||
}).val(function(val){ // and THEN still retrieve it.
|
||||
expect(val.now).to.be('exists');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('get not kick val when it already exists', function(done){ console.log("Undo this!"); return done(); // TODO
|
||||
it('get not kick val when it already exists', function(done){
|
||||
gun.get("some/empty/thing").not(function(){
|
||||
this.put({now: 'THIS SHOULD NOT HAPPEN'});
|
||||
return this.put({now: 'THIS SHOULD NOT HAPPEN'});
|
||||
}).val(function(val){
|
||||
expect(val.now).to.be('exists');
|
||||
done();
|
||||
@ -819,13 +995,12 @@ describe('Gun', function(){
|
||||
|
||||
it('put path val sub', function(done){
|
||||
gun.put({last: {some: 'object'}}).path('last').val(function(val){
|
||||
console.log("fat hat bat", val);
|
||||
expect(val.some).to.be('object');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('get put null', function(done){ console.log("Undo this!"); return done(); // TODO: BUG! WARNING: Occsionally failing, timing issue.
|
||||
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){
|
||||
@ -858,13 +1033,13 @@ describe('Gun', function(){
|
||||
}, 100);
|
||||
});
|
||||
|
||||
it('get not put val path val', function(done){ console.log("Undo this?"); return done(); // TODO // stickies issue
|
||||
it('get not put val path val', function(done){
|
||||
gun.get("examples/list/foobar").not(function(){
|
||||
this.put({
|
||||
id: 'foobar',
|
||||
title: 'awesome title',
|
||||
todos: {}
|
||||
});
|
||||
return this.put({
|
||||
id: 'foobar',
|
||||
title: 'awesome title',
|
||||
todos: {hi: 'you'} // TODO: BUG! This should be empty?
|
||||
}).key("examples/list/foobar");
|
||||
}).val(function(data){
|
||||
expect(data.id).to.be('foobar');
|
||||
}).path('todos').val(function(todos){
|
||||
@ -872,7 +1047,7 @@ describe('Gun', function(){
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('put circular ref', function(done){
|
||||
var data = {};
|
||||
data[0] = "DATA!";
|
||||
@ -913,11 +1088,9 @@ describe('Gun', function(){
|
||||
amber.pet = cat;
|
||||
cat.owner = mark;
|
||||
cat.master = amber;
|
||||
|
||||
gun.put(mark, function(err, ok){
|
||||
expect(err).to.not.be.ok();
|
||||
}).val(function(val){
|
||||
console.log(val);
|
||||
expect(val.age).to.be(23);
|
||||
expect(val.name).to.be("Mark Nadal");
|
||||
expect(Gun.is.soul(val.wife)).to.be.ok();
|
||||
@ -947,7 +1120,6 @@ describe('Gun', function(){
|
||||
expect(mark.citizen).to.be("USA");
|
||||
|
||||
this.path('wife').val(function(Amber){
|
||||
console.log('wife val', Amber);
|
||||
expect(Amber.name).to.be("Amber");
|
||||
expect(Amber.age).to.be(23);
|
||||
expect(Amber.citizen).to.be("USA");
|
||||
@ -1018,7 +1190,6 @@ describe('Gun', function(){
|
||||
|
||||
it('context node and field, put node', function(done){
|
||||
bar.path('combo').put({another: 'node'}).val(function(obj){
|
||||
console.log("oh boy", obj);
|
||||
expect(obj.another).to.be('node');
|
||||
bar.val(function(node){
|
||||
expect(Gun.is.soul(node.combo)).to.be.ok();
|
||||
|
127
web/notes-keys.txt
Normal file
127
web/notes-keys.txt
Normal file
@ -0,0 +1,127 @@
|
||||
Alice comes online and does
|
||||
|
||||
`var todo = gun.get('todo')`
|
||||
|
||||
However she is the first peer, objectively, to be around.
|
||||
|
||||
Therefore, very quickly her query returns empty. So when she
|
||||
|
||||
`todo.put({first: "buy groceries"}).key('todo')`
|
||||
|
||||
the put has to generate a soul and GUN is instructed to associate 'todo' with that soul.
|
||||
|
||||
A few hours later, Bob comes online and does
|
||||
|
||||
`var todo = gun.get('todo')`
|
||||
|
||||
and thankfully he was connected to Alice so he gets her soul and node. So when he
|
||||
|
||||
`todo.put({last: "sell leftovers"}).key('todo')`
|
||||
|
||||
this was the expected and intended result, producing the following graph:
|
||||
|
||||
```{
|
||||
'ASDF': {
|
||||
_: {'#': 'ASDF', '>': {
|
||||
first: 1,
|
||||
last: 2
|
||||
}},
|
||||
first: "buy groceries",
|
||||
last: "sell leftovers"
|
||||
}
|
||||
}```
|
||||
|
||||
For purposes of clarity, we are using states as if they were linearizable (this is not actually the case though).
|
||||
|
||||
Then Carl comes online and tries to
|
||||
|
||||
`var todo = gun.get('todo')`
|
||||
|
||||
But since he is not connected to Alice or Bob, gets an empty result.
|
||||
|
||||
Carl does nothing with it, meaning no mutation, no soul, no generation.
|
||||
|
||||
Then Dave comes online and does the same as everyone else:
|
||||
|
||||
`var todo = gun.get('todo')`
|
||||
|
||||
But Dave is only connected to Carl as a peer, therefore his get is empty. Like Alice, he then
|
||||
|
||||
`todo.put({remember: "eat food!", last: "no leftovers!"}).key('todo')`
|
||||
|
||||
Which unfortunately causes a new soul to be generated. Meanwhile, everybody then does the following:
|
||||
|
||||
`todo.on(function(val){ console.log(val) })`
|
||||
|
||||
Alice and Bob immediately get:
|
||||
|
||||
```{
|
||||
_: {'#': 'ASDF', '>': {
|
||||
first: 1,
|
||||
last: 2
|
||||
}},
|
||||
first: "buy groceries",
|
||||
last: "sell leftovers"
|
||||
}```
|
||||
|
||||
But Carl and Dave immediately get:
|
||||
|
||||
```{
|
||||
_: {'#': 'FDSA', '>': {
|
||||
remember: 3,
|
||||
last: 3
|
||||
}},
|
||||
remember: "eat food!",
|
||||
last: "no leftovers!"
|
||||
}```
|
||||
|
||||
However, a few hours later everybody gets connected. This is the graph everyone then has:
|
||||
|
||||
```{
|
||||
'ASDF': {
|
||||
_: {'#': 'ASDF', '>': {
|
||||
first: 1,
|
||||
last: 2
|
||||
}},
|
||||
first: "buy groceries",
|
||||
last: "sell leftovers"
|
||||
},
|
||||
'FDSA': {
|
||||
_: {'#': 'FDSA', '>': {
|
||||
remember: 3,
|
||||
last: 3
|
||||
}},
|
||||
remember: "eat food!",
|
||||
last: "no leftovers!"
|
||||
}
|
||||
}```
|
||||
|
||||
But their `on` function triggers again, with the following `val` locally for everyone:
|
||||
|
||||
```{
|
||||
first: "buy groceries",
|
||||
remember: "eat food!",
|
||||
last: "no leftovers!"
|
||||
}```
|
||||
|
||||
GUN merges all the nodes with matching keys into a temporary in-memory object.
|
||||
|
||||
This way it is safe to get empty results and still put data into it,
|
||||
|
||||
Everyone will see a unified view of the data when they do get connected, as intended.
|
||||
|
||||
This solves the null, singular, and plural problems for get all together.
|
||||
|
||||
However, if we intentionally do not want to see a potentially conflicting unified view, any peer can:
|
||||
|
||||
`var todos = gun.all('todo')`
|
||||
|
||||
And have the discrete data via:
|
||||
|
||||
`todos.map(function(todo, soul){ console.log(todo) })`
|
||||
|
||||
Which would currently get called twice, with the distinct nodes of 'ASDF' and 'FDSA'.
|
||||
|
||||
The only thing that this does not address is how write operations (put/key) would effect `get` nodes.
|
||||
|
||||
However, I feel like finding the answer to that question will be much easier than trying to solve `get` in any other way.
|
Loading…
x
Reference in New Issue
Block a user