diff --git a/examples/angular/index.html b/examples/angular/index.html
index 5220a6cc..f4bd3bb1 100644
--- a/examples/angular/index.html
+++ b/examples/angular/index.html
@@ -54,9 +54,8 @@
angular.module('admin', []).controller('editor', function($scope){
$scope.data = {};
$scope.$data = gun.load('example/angular/todo').blank(function(){
- console.log("Initializing Data!", this);
- this.set({}).key('example/angular/todo');
- window.location = window.location;
+ console.log("Initializing Data!");
+ this.set({});
}).on(function(data){
Gun.obj.map(data, function(val, field){
if(val === $scope.data[field]){ return }
diff --git a/gun.js b/gun.js
index d5245ddf..1cd5d130 100644
--- a/gun.js
+++ b/gun.js
@@ -216,7 +216,7 @@
gun.__.opt.peers = opt.peers || gun.__.opt.peers || {};
gun.__.opt.uuid = opt.uuid || gun.__.opt.uuid || {};
gun.__.opt.hooks = gun.__.opt.hooks || {};
- gun.__.hook = Gun.shot('next','end');
+ gun.__.hook = Gun.shot('then','end');
Gun.obj.map(opt.hooks, function(h, f){
if(!Gun.fns.is(h)){ return }
gun.__.opt.hooks[f] = h;
@@ -230,9 +230,7 @@
gun.back = from;
gun.__ = from.__;
gun._ = {};
- Gun.obj.map(from._, function(val, field){
- gun._[field] = val;
- });
+ //Gun.obj.map(from._, function(val, field){ gun._[field] = val });
return gun;
}
Chain.load = function(key, cb, opt){
@@ -241,36 +239,36 @@
gun.shot.done(function(){
opt = opt || {};
cb = cb || function(){};
- cb.soul = (key||{})[Gun._.soul];
- if(cb.soul){
- cb.node = gun.__.graph[cb.soul];
- } else {
- //gun._.key = key;
- cb.node = gun.__.keys[key];
+ cb.soul = (key||{})[Gun._.soul]; // is this a key or a soul?
+ if(cb.soul){ // if a soul...
+ cb.node = gun.__.graph[cb.soul]; // then attempt to grab it directly from cache in the graph
+ } else { // if not...
+ cb.node = gun.__.keys[key]; // attempt to grab it directly from our key cache
+ (gun._.keys = gun._.keys || {})[key] = cb.node? 1 : 0; // set a key marker on it
}
- if(!opt.force && cb.node){ // set this to the current node, too!
- Gun.log.call(gun, "load via gun"); // remember to do all the same stack stuff here also!
- gun._.node = cb.node;
- if(Gun.fns.is(cb)){ cb.call(gun, null, Gun.obj.copy(gun._.node)) } // frozen copy
- gun.shot('then').fire(gun._.node); // freezing for internal use is NOT okay.
- return; // TODO: BUG: This needs to react the same as below! I think this is all done/resolved/clear now.
+ if(!opt.force && cb.node){ // if it was in cache, then...
+ Gun.log.call(gun, "load via gun");
+ gun._.node = cb.node; // assign it to this context
+ cb.call(gun, null, Gun.obj.copy(gun._.node)); // frozen copy
+ gun.shot('then').fire();
+ return;
}
cb.fn = function(){}
// missing: hear shots! I now hook this up in other places, but we could get async/latency issues?
// We need to subscribe early? Or the transport layer handle this for us?
if(Gun.fns.is(gun.__.opt.hooks.load)){
gun.__.opt.hooks.load(key, function(err, data){
- //console.log('loaded', err, data, gun);
- gun._.loaded = (gun._.loaded || 0) + 1; // TODO: loading should be idempotent even if we got an err or no data
- if(err){ return cb(err), (gun._.err||cb.fn).call(gun, err) }
- //if(!data){ return gun.shot('then').fire() } // branch blank-kick will be using this, will merge when done.
- if(!data){ return cb(null), (gun._.blank||cb.fn).call(gun) }
- var context = gun.union(data); // safely transform the data
- if(context.err){ return cb(context.err), (gun._.err||cb.fn).call(gun, context.err) }
- gun._.node = gun.__.graph[data._[Gun._.soul]]; // don't wait for the union to be done because we want the immediate state not the intended state.
- if(!cb.soul){ gun.__.keys[key] = gun._.node } // TODO: BUG: what if the key has changed since we were gone? What do we resolve to? Not sure yet.
- if(Gun.fns.is(cb)){ cb.call(gun, null, Gun.obj.copy(gun._.node)) } // frozen copy
- gun.shot('then').fire(gun._.node); // freezing for internal use is NOT okay.
+ if(err){ return cb.call(gun, err), (gun._.err||cb.fn).call(gun, err) }
+ if(!data){ return cb.call(gun, err, data), gun.shot('then').fire() } // if no data, emit without any contexxt change
+ var context = gun.union(data); // safely transform the data into the current context
+ if(context.err){ return cb.call(gun, context.err), (gun._.err||cb.fn).call(gun, context.err) } // but validate it in case of errors
+ gun._.node = gun.__.graph[data._[Gun._.soul]]; // immediately use the state in cache, no waiting for union to be done.
+ if(!cb.soul){ // and if we had loaded with a key rather than a soul
+ gun._.keys[key] = 1; // then set a marker that this key matches
+ gun.__.keys[key] = gun._.node; // and cache a pointer to the node
+ }
+ cb.call(gun, null, Gun.obj.copy(gun._.node)); // frozen copy
+ gun.shot('then').fire();
}, opt);
} else {
Gun.log.call(gun, "Warning! You have no persistence layer to load from!");
@@ -281,29 +279,29 @@
}
Chain.key = function(key, cb){
var gun = this;
- cb = cb || function(){};
gun.shot.then(function(){
- if(Gun.obj.is(key)){ // if key is an object then we get the soul directly from it.
- Gun.obj.map(key, function(soul, field){ return key = field, cb.soul = soul });
- cb.node = gun.__.keys[key] = gun.__.graph[cb.soul]; // if it is cached, then it is important to reference it.
- } else { // else let's get the soul directly from the node, plus link the key.
- cb.node = gun.__.keys[key] = gun._.node;
+ cb = cb || function(){};
+ if(Gun.obj.is(key)){ // if key is an object then we get the soul directly from it
+ Gun.obj.map(key, function(soul, field){ return cb.key = field, cb.soul = soul });
+ } else { // or else
+ cb.key = key; // the key is the key
+ }
+ if(gun._.node){ // if it is in cache
+ cb.soul = gun._.node._[Gun._.soul];
+ (gun._.keys = gun._.keys || {})[cb.key] = 1; // clear the marker in this context
+ (gun.__.keys = gun.__.keys || {})[cb.key] = gun._.node; // and create the pointer
+ } else { // if it is not in cache
+ (gun._.keys = gun._.keys || {})[cb.key] = 0; // then set a marker on this context
}
if(Gun.fns.is(gun.__.opt.hooks.key)){
- gun.__.opt.hooks.key(key, cb.soul || (cb.node||{_:{}})._[Gun._.soul], function(err, data){
- gun.__.keys[key] = cb.node || gun._.node; // once more for good luck.
- if(err){ return cb(err) }
- return cb(null);
+ gun.__.opt.hooks.key(cb.key, cb.soul, function(err, data){
+ if(err){ return cb.call(gun, err) }
+ return cb.call(gun, null);
});
} else {
Gun.log.call(gun, "Warning! You have no key hook!");
}
});
- if(!gun.back){
- //console.log("what what?");
- (gun._.key = gun._.key || {})[key] = true;
- //cb({err: "There exists nothing for the key to reference."});
- }
return gun;
}
/*
@@ -324,56 +322,56 @@
Chain.path = function(path){ // The focal point follows the path
var gun = this.chain();
path = (path || '').split('.');
- gun.back.shot.then(function trace(node){ // should handle blank and err! Err already handled?
- if(!node){ return gun.shot('then').fire() } // TODO: BUG? ERROR? MAYBE? MARK?
- gun.field = null;
- gun._.node = node;
- if(!path.length){ // if the path resolves to another node, we finish here.
- return gun.shot('then').fire(node); // this is not frozen yet, but it is still used for internals so keep it unfrozen.
- }
- var field = Gun.text.ify(path.shift())
- , val = node[field];
- gun.field = field;
- //console.log('path');
- if(Gun.is.soul(val)){ // we might end on a link, so we must resolve
- return gun.load(val).shot.then(trace);
- } else
- if(path.length){ // we cannot go any further, despite the fact there is more path, which means the thing we wanted does not exist.
- gun.shot('then').fire();
- } else { // we are done, and this should be the value we wanted.
- gun.shot('then').fire(node, field); // js copies primitive values, thus we must pass by reference.
- }
+ gun.back.shot.then(function trace(){ // should handle blank and err! Err already handled?
+ if(!gun._.node && !gun.back._.node){ return gun.shot('then').fire() } // TODO: BUG? ERROR? MAYBE? MARK?
+ gun._.field = null;
+ gun._.node = gun._.node || gun.back._.node;
+ if(!path.length){ // if the path resolves to another node, we finish here.
+ return gun.shot('then').fire(); // this is not frozen yet, but it is still used for internals so keep it unfrozen.
+ }
+ var field = Gun.text.ify(path.shift())
+ , val = gun._.node[field];
+ gun._.field = field;
+ if(Gun.is.soul(val)){ // we might end on a link, so we must resolve
+ return gun.load(val).shot.then(trace);
+ } else
+ if(path.length){ // we cannot go any further, despite the fact there is more path, which means the thing we wanted does not exist.
+ gun.shot('then').fire();
+ } else { // we are done, and this should be the value we wanted.
+ gun.shot('then').fire();
+ }
});
return gun;
}
Chain.get = function(cb){
var gun = this;
- gun.shot.then(function(node){
+ gun.shot.then(function(){
cb = cb || function(){};
- cb.call(gun, gun.field? node[gun.field] : Gun.obj.copy(node)); // frozen copy
- // TODO! BUG! Maybe? Should a field that is null trigger a blank instead?
+ if(!gun._.node){ return }
+ cb.call(gun, gun._.field? gun._.node[gun._.field] : Gun.obj.copy(gun._.node), gun._.field); // frozen copy
+ // TODO: BUG! Maybe? Interesting, if delta is an object, I might have to .load!
});
return gun;
}
Chain.on = function(cb){
var gun = this;
- gun.get(function(node){
- var get = this;
+ gun.shot.then(function(){
cb = cb || function(){};
- cb.call(get, Gun.obj.copy(node)); // frozen copy
- get.__.on(get._.node._[Gun._.soul]).event(function(delta){
+ if(!gun._.node){ return }
+ cb.call(gun, gun._.field? gun._.node[gun._.field] : Gun.obj.copy(gun._.node), gun._.field); // frozen copy
+ gun.__.on(gun._.node._[Gun._.soul]).event(function(delta){
if(!delta){ return }
- if(!get.field){
- cb.call(get, Gun.obj.copy(get._.node)); // frozen copy
+ if(!gun._.field){
+ cb.call(gun, Gun.obj.copy(gun._.node)); // frozen copy
return;
}
- if(Gun.obj.has(delta, get.field)){
- delta = delta[get.field];
- cb.call(get, Gun.obj.is(delta)? Gun.obj.copy(delta) : delta); // frozen copy
+ if(Gun.obj.has(delta, gun._.field)){
+ delta = delta[gun._.field];
+ cb.call(gun, Gun.obj.is(delta)? Gun.obj.copy(delta) : delta, gun._.field); // frozen copy
// TODO: BUG! Maybe? Interesting, if delta is an object, I might have to .load!
}
})
- })
+ });
return gun;
}
/*
@@ -388,42 +386,45 @@
If this causes any application-level concern, it can compare against the live data by immediately reading it, or accessing the logs if enabled.
*/
Chain.set = function(val, cb, opt){ // TODO: need to turn deserializer into a trampolining function so stackoverflow doesn't happen.
- opt = opt || {};
var gun = this, set = {};
gun.shot.then(function(){
- console.log("meow?", val);
- cb = Gun.fns.is(cb)? cb : function(){};
- if(gun.field){ // if a field exists, it should always be a string
+ opt = opt || {};
+ cb = cb || function(){};
+ if(gun._.field){ // if a field exists, it should always be a string
var partial = {}; // in case we are doing a set on a field, not on a node
- partial[gun.field] = val; // we create a blank node with the field/value to be set
+ partial[gun._.field] = val; // we create a blank node with the field/value to be set
val = partial;
} else
if(!Gun.obj.is(val)){
- return cb({err: "No field exists to set the " + (typeof val) + " on."}), gun;
+ return cb.call(gun, {err: "No field exists to set the " + (typeof val) + " on."});
}
// TODO: should be able to handle val being a relation or a gun context or a gun promise.
// TODO: BUG: IF we are setting an object, doing a partial merge, and they are reusing a frozen copy, we need to do a DIFF to update the HAM! Or else we'll get "old" HAM.
val._ = Gun.ify.soul.call(gun, {}, gun._.node || val); // set their souls to be the same that way they will merge correctly for us during the union!
set = Gun.ify.call(gun, val, set);
cb.root = set.root;
- if(set.err || !cb.root){ return cb(set.err || {err: "No root object!"}), gun }
+ if(set.err || !cb.root){ return cb.call(gun, set.err || {err: "No root object!"}) }
set = Gun.ify.state(set.nodes, Gun.time.is()); // set time state on nodes?
- if(set.err){ return cb(set.err), gun }
+ if(set.err){ return cb.call(gun, set.err) }
gun.union(set.nodes); // while this maybe should return a list of the nodes that were changed, we want to send the actual delta
gun._.node = gun.__.graph[cb.root._[Gun._.soul]] || cb.root;
- //console.log("Did we set?", cb.root, gun._.key);
- // TODO? ^ Maybe BUG! if val is a new node on a field, _.node should now be that! Or will that happen automatically?
+ if(!gun._.field){
+ Gun.obj.map(gun._.keys, function(yes, key){
+ if(yes){ return }
+ gun.key(key); // TODO: Feature? what about these callbacks?
+ });
+ }
if(Gun.fns.is(gun.__.opt.hooks.set)){
gun.__.opt.hooks.set(set.nodes, function(err, data){ // now iterate through those nodes to a persistence layer and get a callback once all are saved
- if(err){ return cb(err) }
- return cb(null);
+ if(err){ return cb.call(gun, err) }
+ return cb.call(gun, null);
});
} else {
Gun.log.call(gun, "Warning! You have no persistence layer to save to!");
}
});
if(!gun.back){
- gun.shot('then').fire(set);
+ gun.shot('then').fire();
}
return gun;
}
@@ -435,10 +436,9 @@
var error, item = Gun(null).set(obj, function(err){ // create the new item in its own context.
error = err; // if this happens, it should get called before the .get
}).get(function(val){
- if(error){ return cb(error) } // which in case it is, allows us to fail fast.
+ if(error){ return cb.call(gun, error) } // which in case it is, allows us to fail fast.
var list = {}, soul = Gun.is.soul.on(val);
- //console.log(val, 'has', soul);
- if(!soul){ return cb({err: "No soul!"}) }
+ if(!soul){ return cb.call(gun, {err: "No soul!"}) }
list[soul] = val; // other wise, let's then
gun.set(list, cb); // merge with the graph node.
});
@@ -446,9 +446,9 @@
}
Chain.map = function(cb, opt){
var gun = this;
- gun.shot.then(function(val){
- cb = cb || function(){};
+ gun.get(function(val){
opt = opt || {};
+ cb = cb || function(){};
Gun.obj.map(val, function(val, field){ // by default it only maps over nodes
if(Gun._.meta == field){ return }
if(Gun.is.soul(val)){
@@ -497,9 +497,20 @@
});
return context;
}
- Chain.blank = function(blank){ // master should have this old version, blank-kick WILL be upgrading this.
- var gun = this;
- gun._.blank = Gun.fns.is(blank)? blank : function(){};
+ Chain.blank = function(blank){
+ var tmp = this;
+ var gun = this.chain();
+ tmp.shot.then(function(){
+ if(tmp._.node){ // if it does indeed exist
+ gun._ = tmp._; // switch back to the original context
+ return gun.shot('then').fire(); // yet fire off the chain
+ }
+ blank.call(tmp); // call the blank function with the original context
+ tmp.shot.then(function(){ // then after the blank logic is done...
+ gun._ = tmp._; // inherit those changes
+ gun.shot('then').fire(); // and fire off the chain
+ });
+ });
return gun;
}
Chain.err = function(dud){ // WARNING: dud was depreciated.
@@ -628,7 +639,6 @@
(chain._ = chain._ || {})[where] = chain._[where] || [];
chain.$[where] = chain.$[where] || function(fn){
if(chain.args){
- //console.log("shoot!", chain.args);
fn.apply(chain, chain.args);
} else {
(chain._[where]||[]).push(fn);
diff --git a/lib/file.js b/lib/file.js
index aa1ac83a..be5b5439 100644
--- a/lib/file.js
+++ b/lib/file.js
@@ -10,7 +10,7 @@ Gun.on('opt').event(function(gun, opts){
opts.file = opts.file || 'data.json';
var fs = require('fs');
- file.raw = file.raw || fs.existsSync(opts.file)? fs.readFileSync(opts.file).toString() : null;
+ file.raw = file.raw || (fs.existsSync||require('path').existsSync)(opts.file)? fs.readFileSync(opts.file).toString() : null;
var all = file.all = file.all || Gun.obj.ify(file.raw || {nodes: {}, keys: {}});
gun.opt({hooks: {
diff --git a/package.json b/package.json
index fa5c7e57..0594601d 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
{ "name": "gun"
-, "version": "0.0.9f"
+, "version": "0.0.9g"
, "author": "Mark Nadal"
, "description": "Graph engine."
, "engines": {
diff --git a/test/common.js b/test/common.js
index 433b28b8..9f60dde2 100644
--- a/test/common.js
+++ b/test/common.js
@@ -316,7 +316,8 @@ describe('Gun', function(){
describe('API', function(){
- var gun = Gun();
+ require('../lib/file');
+ var gun = Gun({file: 'data.json'});
it('set key get', function(done){
gun.set({hello: "world"}).key('hello/world').get(function(val){
@@ -352,45 +353,73 @@ describe('Gun', function(){
done();
});
});
- it.skip('var set key path', function(done){ // contexts should be able to be saved to a variable
- var foo = gun.set({foo: 'bar'}).key('foo/bar');
- foo.path('hello.world.nowhere')// this should only change the context temporarily
- setTimeout(function(){
- foo.path('foo').get(function(val){ // and then be reused from the same original context
- console.log('?', val);
- expect(val).to.be('bar'); // this should work
- done();
- });
- }, 100);
- });
- it.skip('var load path', function(done){ // contexts should be able to be saved to a variable
- var foo = gun.load('foo/bar');
- foo.path('hello.world.nowhere')// this should only change the context temporarily
- setTimeout(function(){
- foo.path('foo').get(function(val){ // and then be reused from the same original context
- expect(val).to.be('bar'); // this should work
- done();
- });
- }, 100);
- });
-
-
- it('path', function(done){
- console.log("fix path!");
- return done(); // TODO: FIX! This is broken because of API changes, fix it!
-
- this.timeout(9000);
- var gun = require('gun')({
- s3: require('./shotgun') // replace this with your own keys!
- });
- gun.load('d1ed426098eae2bba8c60605e1e4552f66281770', null, {id: true}) // get Rodney Morris
- .path('parent.parent.first') // Rodney's parent is Juan Colon, whose parent is Francis Peters
- .get(function(val){
- console.log("val!", val);
+ it('load path empty set', function(done){
+ gun.load('hello/world').path('earth').set('mars').get(function(val){
+ expect(val).to.be('mars');
done();
});
- console.log("________________________");
+ });
+
+ it('load path get', function(done){
+ gun.load('hello/world').path('earth').get(function(val){
+ expect(val).to.be('mars');
+ done();
+ });
+ });
+
+ it('key set get', function(done){
+ gun.key('world/hello').set({world: "hello"}).get(function(val){
+ expect(val.world).to.be('hello');
+ done();
+ });
+ });
+
+ it('load', function(done){
+ gun.load('world/hello').get(function(val){
+ expect(val.world).to.be('hello');
+ done();
+ });
+ });
+
+ it('load blank kick get', function(done){ // it would be cool with GUN
+ gun.load("some/empty/thing").blank(function(){ // that if you call blank first
+ this.set({now: 'exists'}); // you can set stuff
+ }).get(function(val){ // and THEN still retrieve it.
+ expect(val.now).to.be('exists');
+ done();
+ });
+ });
+
+ it('load blank kick get when it already exists', function(done){
+ gun.load("some/empty/thing").blank(function(){
+ this.set({now: 'THIS SHOULD NOT HAPPEN'});
+ }).get(function(val){
+ expect(val.now).to.be('exists');
+ done();
+ });
+ });
+
+ it('var set key path', function(done){ // contexts should be able to be saved to a variable
+ var foo = gun.set({foo: 'bar'}).key('foo/bar');
+ foo.path('hello.world.nowhere'); // this should become a sub-context, that doesn't alter the original
+ setTimeout(function(){
+ foo.path('foo').get(function(val){ // and then the original should be able to be reused later
+ expect(val).to.be('bar'); // this should work
+ done();
+ });
+ }, 100);
+ });
+
+ it('var load path', function(done){ // contexts should be able to be saved to a variable
+ var foo = gun.load('foo/bar');
+ foo.path('hello.world.nowhere'); // this should become a sub-context, that doesn't alter the original
+ setTimeout(function(){
+ foo.path('foo').get(function(val){ // and then the original should be able to be reused later
+ expect(val).to.be('bar'); // this should work
+ done();
+ });
+ }, 100);
});
});