insert, map, fixes, working on path

This commit is contained in:
Mark Nadal 2015-01-20 06:17:12 -07:00
parent 146ee003d7
commit 22aea79838
9 changed files with 108 additions and 92 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
node_modules/* node_modules/*
npm-debug.log npm-debug.log
*data.json

View File

@ -6,7 +6,12 @@ var app = express();
var Gun = require('gun'); var Gun = require('gun');
var gun = Gun({ var gun = Gun({
s3: (process.env.NODE_ENV === 'production')? null : require('../test/shotgun') // replace this with your own keys! file: 'data.json',
s3: {
key: '', // AWS Access Key
secret: '', // AWS Secret Token
bucket: '' // The bucket you want to save into
}
}); });
gun.attach(app); gun.attach(app);

View File

@ -51,9 +51,12 @@
</ul> </ul>
<script> <script>
var gun = Gun(location.origin + '/gun'); var gun = Gun(location.origin + '/gun');
angular.module('admin', []).controller('editor', function($scope){ angular.module('admin', []).controller('editor', function($scope){ //return;
$scope.data = {}; $scope.data = {};
$scope.$data = gun.load('blob/data').on(function(data){ $scope.$data = gun.load('example/angular/todo').blank(function(){
console.log("Initializing Data!")
gun.set({chicken: 'feet'}).key('example/angular/todo');
}).on(function(data){
Gun.obj.map(data, function(val, field){ Gun.obj.map(data, function(val, field){
if(val === $scope.data[field]){ return } if(val === $scope.data[field]){ return }
$scope.data[field] = val; $scope.data[field] = val;
@ -73,4 +76,4 @@
}); });
</script> </script>
</body> </body>
</html> </html>

153
gun.js
View File

@ -280,18 +280,17 @@
} }
Chain.key = function(key, cb){ Chain.key = function(key, cb){
var gun = this; var gun = this;
cb = cb || function(){};
gun.shot.then(function(){ gun.shot.then(function(){
cb = cb || function(){}; if(Gun.obj.is(key)){ // if key is an object then we get the soul directly from it.
if(Gun.obj.is(key)){ // if key is an object then we get the soul directly from it because the node might not exist in cache.
Gun.obj.map(key, function(soul, field){ return key = field, cb.soul = soul }); 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. cb.node = gun.__.keys[key] = gun.__.graph[cb.soul]; // if it is cached, then it is important to reference it.
} else { // else the node does exist in cache and we will get the soul from it instead, plus link the key. } else { // else let's get the soul directly from the node, plus link the key.
cb.node = gun.__.keys[key] = gun._.node; cb.node = gun.__.keys[key] = gun._.node;
} }
if(Gun.fns.is(gun.__.opt.hooks.key)){ if(Gun.fns.is(gun.__.opt.hooks.key)){
gun.__.opt.hooks.key(key, cb.soul || (cb.node||{_:{}})._[Gun._.soul], function(err, data){ gun.__.opt.hooks.key(key, cb.soul || (cb.node||{_:{}})._[Gun._.soul], function(err, data){
//Gun.log.call(gun, "key made", key); gun.__.keys[key] = cb.node || gun._.node; // once more for good luck.
gun.__.keys[key] = cb.node; // once more for good luck.
if(err){ return cb(err) } if(err){ return cb(err) }
return cb(null); return cb(null);
}); });
@ -299,7 +298,9 @@
Gun.log.call(gun, "Warning! You have no key hook!"); Gun.log.call(gun, "Warning! You have no key hook!");
} }
}); });
if(!gun.back){ gun.shot('then').fire(gun._.node) } if(!gun.back){
cb({err: "There exists nothing for the key to reference."});
}
return gun; return gun;
} }
/* /*
@ -321,7 +322,7 @@
var gun = this.chain(); var gun = this.chain();
path = (path || '').split('.'); path = (path || '').split('.');
gun.back.shot.then(function trace(node){ // should handle blank and err! Err already handled? gun.back.shot.then(function trace(node){ // should handle blank and err! Err already handled?
//console.log("shot path", path, node); console.log("path happening"); // MARK COME BACK HERE!!!
gun.field = null; gun.field = null;
gun._.node = node; gun._.node = node;
if(!path.length){ // if the path resolves to another node, we finish here. if(!path.length){ // if the path resolves to another node, we finish here.
@ -339,8 +340,8 @@
gun.shot('then').fire(val); // internals use this thus not frozen yet, but primitive values are passed as copies anyways in js. gun.shot('then').fire(val); // internals use this thus not frozen yet, but primitive values are passed as copies anyways in js.
} }
}); });
// if(!gun.back){ gun.shot('then').fire() } // replace below with this? maybe??? if(gun.back && gun.back._ && gun.back._.loaded){ // is this causing a double trigger?
if(gun.back && gun.back._ && gun.back._.loaded){ console.log("path back");
gun._.node = gun.back._.node; gun._.node = gun.back._.node;
gun.back.shot('then').fire(gun.back._.node); gun.back.shot('then').fire(gun.back._.node);
} }
@ -349,7 +350,8 @@
Chain.get = function(cb){ Chain.get = function(cb){
var gun = this; var gun = this;
gun.shot.then(function(val){ gun.shot.then(function(val){
console.log("did I get it?", val, gun); console.log("get happening", val);
if(!gun._.node){ return } // I think this is safe?
cb = cb || function(){}; cb = cb || function(){};
cb.call(gun, Gun.obj.is(val)? Gun.obj.copy(val) : val); // frozen copy cb.call(gun, Gun.obj.is(val)? Gun.obj.copy(val) : val); // frozen copy
}); });
@ -358,8 +360,9 @@
Chain.on = function(cb){ Chain.on = function(cb){
var gun = this; var gun = this;
gun.get(function(node){ gun.get(function(node){
var get = this; // I'm not sure what state we're in, or which (get or gun) we should this-ify. var get = this;
cb.call(gun, Gun.obj.copy(node)); // frozen copy cb = cb || function(){};
cb.call(get, Gun.obj.copy(node)); // frozen copy
get.__.on(get._.node._[Gun._.soul]).event(function(delta){ get.__.on(get._.node._[Gun._.soul]).event(function(delta){
if(!delta){ return } if(!delta){ return }
if(!get.field){ if(!get.field){
@ -387,75 +390,67 @@
*/ */
Chain.set = function(val, cb, opt){ // TODO: need to turn deserializer into a trampolining function so stackoverflow doesn't happen. Chain.set = function(val, cb, opt){ // TODO: need to turn deserializer into a trampolining function so stackoverflow doesn't happen.
opt = opt || {}; opt = opt || {};
var gun = this, set; var gun = this, set = {};
if(gun.field){ // field is always a string gun.shot.then(function(){
set = {}; // in case we are doing a set on a field, not on a node cb = Gun.fns.is(cb)? cb : function(){};
set[gun.field] = val; // we create a blank node with the field/value to be set if(gun.field){ // if a field exists, it should always be a string
val = set; var partial = {}; // in case we are doing a set on a field, not on a node
} else partial[gun.field] = val; // we create a blank node with the field/value to be set
if(!Gun.obj.is(val)){ val = partial;
return cb({err: "No field exists to set the " + (typeof val) + " on."}), cb.root; } else
} if(!Gun.obj.is(val)){
// TODO: should be able to handle val being a relation or a gun context or a gun promise. return cb({err: "No field exists to set the " + (typeof val) + " on."}), gun;
// 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! // TODO: should be able to handle val being a relation or a gun context or a gun promise.
cb = Gun.fns.is(cb)? cb : function(){}; // 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.
set = Gun.ify.call(gun, val); 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!
cb.root = set.root; set = Gun.ify.call(gun, val, set);
console.log("chain.set root", cb.root); cb.root = set.root;
if(set.err){ return cb(set.err), gun } if(set.err || !cb.root){ return cb(set.err || {err: "No root object!"}), gun }
set = Gun.ify.state(set.nodes, Gun.time.is()); // set time state on nodes? set = Gun.ify.state(set.nodes, Gun.time.is()); // set time state on nodes?
if(set.err){ return cb(set.err), gun } if(set.err){ return cb(set.err), gun }
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.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(cb.root);
console.log("still rolling", gun._.node); gun._.node = gun.__.graph[cb.root._[Gun._.soul]] || cb.root;
// TODO? ^ Maybe BUG! if val is a new node on a field, _.node should now be that! Or will that happen automatically? // TODO? ^ Maybe BUG! if val is a new node on a field, _.node should now be that! Or will that happen automatically?
if(Gun.fns.is(gun.__.opt.hooks.set)){ if(Gun.fns.is(gun.__.opt.hooks.set)){
gun.__.opt.hooks.set(set.nodes, function(err, data){ // now iterate through those nodes to a persistence layer and get a callback once all are saved gun.__.opt.hooks.set(set.nodes, function(err, data){ // now iterate through those nodes to a persistence layer and get a callback once all are saved
if(err){ return cb(err) } if(err){ return cb(err) }
return cb(null); return cb(null);
}); });
} else { } else {
Gun.log.call(gun, "Warning! You have no persistence layer to save to!"); Gun.log.call(gun, "Warning! You have no persistence layer to save to!");
} }
gun.shot.then(function(){ // set/key should cause a subscription, is this working yet?
}); });
if(!gun.back){ if(!gun.back){
gun.shot('then').fire(); gun.shot('then').fire(set);
console.log("gun.set() !gun.back fire()");
} }
return gun; return gun;
} }
Chain.insert = function(obj, cb, opt){ Chain.insert = function(obj, cb, opt){
var gun = this; var gun = this;
if(!gun.back){ opt = opt || {};
console.log("no back!"); cb = cb || function(){};
gun = gun.set({}); gun = gun.set({}); // insert assumes a graph node. So either create it or merge with the existing one.
} var error, item = Gun(null).set(obj, function(err){ // create the new item in its own context.
console.log("back!!", gun); error = err; // if this happens, it should get called before the .get
gun.shot.then(function(){ }).get(function(val){
opt = opt || {}; if(error){ return cb(error) } // which in case it is, allows us to fail fast.
cb = cb || function(){}; var list = {}, soul = Gun.is.soul.on(val);
var item = gun.chain().set(obj).get(function(val){ console.log(val, 'has', soul);
var list = {}, soul = Gun.is.soul.on(val); if(!soul){ return cb({err: "No soul!"}) }
console.log(val, 'has', soul); list[soul] = val; // other wise, let's then
if(!soul){ return } // TODO: BUG!!! Handle this edge case!!! gun.set(list, cb); // merge with the graph node.
list[soul] = val;
gun.set(list);
});
}); });
return gun; return gun;
} }
/*
Chain.map = function(cb, opt){ Chain.map = function(cb, opt){
var gun = this; var gun = this;
gun.shot.then(function(val){ gun.shot.then(function(val){
cb = cb || function(){}; cb = cb || function(){};
opt = opt || {}; opt = opt || {};
Gun.obj.map(val, function(obj, soul){ Gun.obj.map(val, function(obj, soul){ // by default it only maps over nodes
if(!Gun.is.soul(obj) && !opt.all){ return } if(!Gun.is.soul(obj) && !opt.all){ return } // {all: true} maps over everything
gun.load(obj).get(function(val){ // should map have support for blank? gun.load(obj).get(function(val){ // should map have support for blank?
cb.call(this, obj, soul); cb.call(this, obj, soul);
}); });
@ -463,7 +458,6 @@
}); });
return gun; return gun;
} }
*/
// Union is different than set. Set casts non-gun style of data into a gun compatible data. // Union is different than set. Set casts non-gun style of data into a gun compatible data.
// Union takes already gun compatible data and validates it for a merge. // Union takes already gun compatible data and validates it for a merge.
// Meaning it is more low level, such that even set uses union internally. // Meaning it is more low level, such that even set uses union internally.
@ -499,10 +493,15 @@
return context; return context;
} }
Chain.blank = function(blank){ Chain.blank = function(blank){
this._.blank = Gun.fns.is(blank)? blank : function(){}; var gun = this;
return this; gun._.blank = Gun.fns.is(blank)? blank : function(){};
gun.shot.then(function(val){
if(val === 0){ return }
if(!val){ gun._.blank() }
});
return gun;
} }
Chain.err = Chain.dud = function(dud){ // WARNING: dud will be depreciated in favor of err. Chain.err = function(dud){ // WARNING: dud was depreciated.
this._.dud = Gun.fns.is(dud)? dud : function(){}; this._.dud = Gun.fns.is(dud)? dud : function(){};
return this; return this;
} }
@ -751,7 +750,8 @@
} }
}({})); }({}));
;(function(Serializer){ ;(function(Serializer){
Gun.ify = function(data){ // TODO: BUG: Modify lists to include HAM state Gun.ify = function(data, bind){ // TODO: BUG: Modify lists to include HAM state
if(Gun.obj.map(bind, function(){ return true })){ return {err: "Bind must be an empty object."} }
var gun = Gun.is(this)? this : {} var gun = Gun.is(this)? this : {}
, context = { , context = {
nodes: {} nodes: {}
@ -767,9 +767,10 @@
return data; return data;
} else } else
if(Gun.obj.is(data)){ if(Gun.obj.is(data)){
var value = {}, symbol = {}, seen var value = bind || {}, symbol = {}, seen
, err = {err: "Metadata does not support external or circular references at " + sub.path, meta: true}; , err = {err: "Metadata does not support external or circular references at " + sub.path, meta: true};
context.root = context.root || value; context.root = context.root || value;
bind = null;
if(seen = ify.seen(context._seen, data)){ if(seen = ify.seen(context._seen, data)){
//Gun.log.call(gun, "seen in _", sub._, sub.path, data); //Gun.log.call(gun, "seen in _", sub._, sub.path, data);
context.err = err; context.err = err;
@ -899,7 +900,7 @@
console.log("gun load", key); console.log("gun load", key);
Gun.obj.map(gun.__.opt.peers, function(peer, url){ Gun.obj.map(gun.__.opt.peers, function(peer, url){
request(url, null, function(err, reply){ request(url, null, function(err, reply){
console.log('via', url, key, reply); //console.log('via', url, key, reply);
if(err || !reply){ return } // handle reconnect? if(err || !reply){ return } // handle reconnect?
if(reply.body && reply.body.err){ if(reply.body && reply.body.err){
cb(reply.body.err); cb(reply.body.err);
@ -975,14 +976,14 @@
} }
tab.set.defer = {}; tab.set.defer = {};
request.createServer(function(req, res){ request.createServer(function(req, res){
console.log("client server received request", req); //console.log("client server received request", req);
if(!req.body){ return } if(!req.body){ return }
gun.union(req.body); gun.union(req.body);
}); });
(function(){ (function(){
tab.store = {}; tab.store = {};
var store = window.localStorage || {setItem: function(){}, removeItem: function(){}, getItem: function(){}}; var store = window.localStorage || {setItem: function(){}, removeItem: function(){}, getItem: function(){}};
tab.store.set = function(key, val){console.log('setting', key); return store.setItem(key, Gun.text.ify(val)) } tab.store.set = function(key, val){ return store.setItem(key, Gun.text.ify(val)) }
tab.store.get = function(key){ return Gun.obj.ify(store.getItem(key)) } tab.store.get = function(key){ return Gun.obj.ify(store.getItem(key)) }
tab.store.del = function(key){ return store.removeItem(key) } tab.store.del = function(key){ return store.removeItem(key) }
}()); }());
@ -1038,7 +1039,7 @@
}catch(e){ return } }catch(e){ return }
if(!res){ return } if(!res){ return }
if(res.wsrid){ (r.ws.cbs[res.wsrid]||function(){})(null, res) } if(res.wsrid){ (r.ws.cbs[res.wsrid]||function(){})(null, res) }
console.log("We have a pushed message!", res); //console.log("We have a pushed message!", res);
if(res.body){ r.createServer(res, function(){}) } // emit extra events. if(res.body){ r.createServer(res, function(){}) } // emit extra events.
}; };
ws.onerror = function(e){ console.log(e); }; ws.onerror = function(e){ console.log(e); };

View File

@ -12,7 +12,7 @@
opt = opt || {}; opt = opt || {};
s.AWS.config.bucket = s.config.bucket = opt.bucket || opt.Bucket || s.config.bucket || process.env.AWS_S3_BUCKET; s.AWS.config.bucket = s.config.bucket = opt.bucket || opt.Bucket || s.config.bucket || process.env.AWS_S3_BUCKET;
s.AWS.config.region = s.config.region = opt.region || s.config.region || process.env.AWS_REGION || "us-east-1"; s.AWS.config.region = s.config.region = opt.region || s.config.region || process.env.AWS_REGION || "us-east-1";
s.AWS.config.accessKeyId = s.config.accessKeyId = opt.key || opt.accessKeyId || s.config.accessKeyId || process.env.AWS_ACCESS_KEY_ID; s.AWS.config.accessKeyId = s.config.accessKeyId = opt.key = opt.key || opt.accessKeyId || s.config.accessKeyId || process.env.AWS_ACCESS_KEY_ID;
s.AWS.config.secretAccessKey = s.config.secretAccessKey = opt.secret || opt.secretAccessKey || s.config.secretAccessKey || process.env.AWS_SECRET_ACCESS_KEY; s.AWS.config.secretAccessKey = s.config.secretAccessKey = opt.secret || opt.secretAccessKey || s.config.secretAccessKey || process.env.AWS_SECRET_ACCESS_KEY;
if(s.config.fakes3 = s.config.fakes3 || opt.fakes3 || process.env.fakes3){ if(s.config.fakes3 = s.config.fakes3 || opt.fakes3 || process.env.fakes3){
s.AWS.config.endpoint = s.config.endpoint = opt.fakes3 || s.config.fakes3 || process.env.fakes3; s.AWS.config.endpoint = s.config.endpoint = opt.fakes3 || s.config.fakes3 || process.env.fakes3;

View File

@ -1,12 +1,18 @@
// This was written by the wonderful Forrest Tait
// twas not designed for production use
// only simple local development.
var Gun = require('../gun'); var Gun = require('../gun'), file = {};
var fs = require('fs');
Gun.on('opt').event(function(gun, opts){ Gun.on('opt').event(function(gun, opts){
if(!opts || (opts.s3 && opts.s3.key)){ return } // don't use this plugin if S3 is being used.
var fs = file.fs = file.fs || require('fs');
var raw = file.raw = file.raw || fs.existsSync(opts.file || data.txt)?
fs.readFileSync(opts.file || 'data.txt').toString()
: null;
var all = file.all = file.all || Gun.obj.ify(raw || {nodes: {}, keys: {}});
var raw = fs.existsSync(opts.file || data.txt) ? fs.readFileSync(opts.file || 'data.txt').toString() : null ;
var all = Gun.obj.ify(raw || {nodes: {}, keys: {}});
gun.opt({hooks: { gun.opt({hooks: {
load: function(key, cb, options){ load: function(key, cb, options){
if(Gun.obj.is(key) && key[Gun._.soul]){ if(Gun.obj.is(key) && key[Gun._.soul]){

View File

@ -3,6 +3,7 @@
var S3 = require('./aws'); var S3 = require('./aws');
Gun.on('opt').event(function(gun, opt){ Gun.on('opt').event(function(gun, opt){
if(!opt || !opt.s3){ return } // don't use S3 if it isn't specified.
opt.s3 = opt.s3 || {}; opt.s3 = opt.s3 || {};
var s3 = gun.__.opt.s3 = gun.__.opt.s3 || S3(opt && opt.s3); var s3 = gun.__.opt.s3 = gun.__.opt.s3 || S3(opt && opt.s3);
s3.prefix = s3.prefix || opt.s3.prefix || ''; s3.prefix = s3.prefix || opt.s3.prefix || '';

View File

@ -3,5 +3,6 @@
var Gun = require('../gun'); var Gun = require('../gun');
require('./s3'); require('./s3');
require('./wsp'); require('./wsp');
require('./file');
module.exports = Gun; module.exports = Gun;
}()); }());

View File

@ -146,11 +146,9 @@
if(!req || !req.url || !req.url.key || !Gun.obj.has(req.body, Gun._.soul)){ return } if(!req || !req.url || !req.url.key || !Gun.obj.has(req.body, Gun._.soul)){ return }
var load = {}, index = {}, soul; var load = {}, index = {}, soul;
soul = load[Gun._.soul] = index[req.url.key] = req.body[Gun._.soul]; soul = load[Gun._.soul] = index[req.url.key] = req.body[Gun._.soul];
gun.load(load, function(e,r){ gun.load(load).key(index, function(err, reply){
gun.key(index, function(err, reply){ if(err){ return cb({headers: {'Content-Type': tran.json}, body: {err: err}}) }
if(err){ return cb({headers: {'Content-Type': tran.json}, body: {err: err}}) } cb({headers: {'Content-Type': tran.json}, body: reply});
cb({headers: {'Content-Type': tran.json}, body: reply});
});
}); });
return true; return true;
} }