tests pass, drivers fixed!

This commit is contained in:
Mark Nadal 2015-07-03 22:48:38 -07:00
parent 566c5c1cd6
commit 2abfc17c14
8 changed files with 304 additions and 301 deletions

125
README.md
View File

@ -1,10 +1,12 @@
gun [![Build Status](https://travis-ci.org/amark/gun.svg?branch=master)](https://travis-ci.org/amark/gun)
gun [![Build Status](https://travis-ci.org/amark/gun.svg?branch=master)](https://travis-ci.org/amark/gun) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/amark/gun?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
===
GUN is a realtime, decentralized, embedded, graph database engine.
## Getting Started
For the browser, try out this [tutorial](https://dl.dropboxusercontent.com/u/4374976/gun/web/think.html). This README is for GUN servers.
If you do not have [node](http://nodejs.org/) or [npm](https://www.npmjs.com/), read [this](https://github.com/amark/gun/blob/master/examples/install.sh) first.
Then in your terminal, run:
@ -35,42 +37,21 @@ These are the default persistence layers, they are modular and can be replaced o
Using S3 is recommended for deployment, and using a file is recommended for local development.
Now you can save your first object, and create a reference to it.
## Demos
```javascript
gun.set({ hello: 'world' }).key('my/first/data');
```
Altogether, try it with the node hello world web server which will reply with your data.
```javascript
var Gun = require('gun');
var gun = Gun({ file: 'data.json' });
gun.set({ hello: 'world' }).key('my/first/data');
var http = require('http');
http.createServer(function(req, res){
gun.load('my/first/data', function(err, data){
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify(data));
});
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');
```
Fire up your browser and hit that URL - you'll see your data, plus some gun specific metadata.
## Examples
Try out some online [examples](http://gunjs.herokuapp.com/) or run them yourself with the following command:
The examples included in this repo are online [here](http://gunjs.herokuapp.com/), you can run them locally by:
```bash
git clone http://github.com/amark/gun
cd gun/examples && npm install
node express.js 8080
sudo npm install gun
cd node_modules/gun
node examples/http.js 8080
```
Then visit [http://localhost:8080](http://localhost:8080) in your browser.
Then visit [http://localhost:8080](http://localhost:8080) in your browser. If that did not work it is probably because npm installed to a global directory, to fix this try `mkdir node_modules` in your desired directory and re-run the above commands.
## WARNINGS
Version 0.2.0 breaks **everything** from 0.1.x, see [#54](/../../issues/54) to migrate (`.all` is not implemented yet). GUN is not stable, and therefore should not be trusted in a production environment.
## API
@ -87,20 +68,20 @@ In gun, it can be helpful to think of everything as field/value pairs. For examp
"email": "mark@gunDB.io"
}
```
Now, we want to save this object to a key called `usernames/marknadal`. We can do that like this:
Now, we want to save this object to a key called `'usernames/marknadal'`. We can do that like this:
```javascript
gun.set({
gun.put({
username: "marknadal",
name: "Mark Nadal",
email: "mark@gunDB.io"
}).key('usernames/marknadal');
```
We can also pass `set` a callback that can be used to handle errors:
We can also pass `put` a callback that can be used to handle errors:
```javascript
gun.set({
gun.put({
username: "marknadal",
name: "Mark Nadal",
email: "mark@gunDB.io"
@ -114,33 +95,40 @@ gun.set({
Once we have some data stored in gun, we need a way to get them out again. Retrieving the data that we just stored would look like this:
```javascript
gun.load('usernames/marknadal').get(function(user){
gun.get('usernames/marknadal').val(function(user){
console.log(user.name); // Prints `Mark Nadal` to the console
});
```
Basically, this tells gun to check `usernames/marknadal`, and then return the object it finds associated with it. For more information, including how to save relational or document based data, [check out the wiki](https://github.com/amark/gun/wiki).
Basically, this tells gun to check `'usernames/marknadal'`, and then return the object it finds associated with it. For more information, including how to save relational or document based data, [check out the wiki](https://github.com/amark/gun/wiki).
---
## YOU
We're just getting started, so join us! Being lonely is never any fun, especially when programming.
I want to help you, because my goal is for GUN to be the easiest database ever.
That means if you ever get stuck on something for longer than 5 minutes,
you should talk to me so I can help you solve it.
Your input will then help me improve gun.
We are also really open to contributions! GUN is easy to extend and customize:
## YOU
Being lonely is never any fun, especially when programming.
Our goal is for GUN to be the easiest database ever,
which means if you ever get stuck on something for longer than 5 minutes,
let us know so we can help you. Your input is invaluable,
as it enables us where to refine GUN. So drop us a line in the [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/amark/gun?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)! Or join the [mail list](https://groups.google.com/forum/#!forum/g-u-n).
Thanks to the following people who have contributed to GUN, via code, issues, or conversation:
[agborkowski](https://github.com/agborkowski), [alexlafroscia](https://github.com/alexlafroscia), [anubiann00b](https://github.com/anubiann00b), [bromagosa](https://github.com/bromagosa), [coolaj86](https://github.com/coolaj86), [d-oliveros](https://github.com/d-oliveros), [danscan](https://github.com/danscan), [forrestjt](https://github.com/forrestjt), [gedw99](https://github.com/gedw99), [HelloCodeMing](https://github.com/HelloCodeMing), [JosePedroDias](https://github.com/josepedrodias), [onetom](https://github.com/onetom), [ndarilek](https://github.com/ndarilek), [phpnode](https://github.com/phpnode), [riston](https://github.com/riston), [rootsical](https://github.com/rootsical), [rrrene](https://github.com/rrrene), [ssr1ram](https://github.com/ssr1ram), [Xe](https://github.com/Xe), [zot](https://github.com/zot)
This list of contributors was manually compiled, alphabetically sorted. If we missed you, please submit an issue so we can get you added!
## Contribute
Extending GUN or writing modules for it is as simple as:
`Gun.on('opt').event(function(gun, opt){ /* Your module here! */ })`
It is also important to us that your database is not a magical black box.
So often our questions get dismissed with "its complicated hard low level stuff, let the experts handle it."
And we do not think that attitude will generate any progress for people.
Instead, we want to make everyone an expert by actually getting really good at explaining the concepts.
So join our community, in the quest of learning cool things and helping yourself and others build awesome technology.
We also want our database to be comprehensible, not some magical black box.
So often database questions get dismissed with "its complicated hard low level stuff, let the experts handle it".
That attitude prevents progress, instead we welcome teaching people and listening to new perspectives.
Join along side us in a quest to learn cool things and help others build awesome technology!
- [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/amark/gun?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) (all chats relating to GUN and development should be here! IRC style)
- Google Group: https://groups.google.com/forum/#!forum/g-u-n (for slower threaded discussions)
We need help on the following roadmap.
## Ahead
- ~~Realtime push to the browser~~
@ -151,40 +139,11 @@ So join our community, in the quest of learning cool things and helping yourself
- Test more
- Bug fixes
- Data Structures:
- ~~Groups~~
- Linked Lists
- Collections (hybrid: linked-groups/paginated-lists)
- ~~Sets~~ (Table/Collections, Unordered Lists)
- CRDTs
- OT
- Query:
- SQL
- MongoDB Query Documents
- Neo4j Cypher
- Gremlin Query Language
## Contributors
Thanks to the following people who have contributed to GUN:
- [agborkowski](https://github.com/agborkowski)
- [alexlafroscia](https://github.com/alexlafroscia)
- [anubiann00b](https://github.com/anubiann00b)
- [bromagosa](https://github.com/bromagosa)
- [coolaj86](https://github.com/coolaj86)
- [d-oliveros](https://github.com/d-oliveros)
- [danscan](https://github.com/danscan)
- [forrestjt](https://github.com/forrestjt)
- [gedw99](https://github.com/gedw99)
- [HelloCodeMing](https://github.com/HelloCodeMing)
- [JosePedroDias](https://github.com/josepedrodias)
- [onetom](https://github.com/onetom)
- [ndarilek](https://github.com/ndarilek)
- [phpnode](https://github.com/phpnode)
- [riston](https://github.com/riston)
- [rootsical](https://github.com/rootsical)
- [rrrene](https://github.com/rrrene)
- [ssr1ram](https://github.com/ssr1ram)
- [Xe](https://github.com/Xe)
- [zot](https://github.com/zot)
This list of contributors was manually compiled; if you have contributed in code, issues, or conversation and your GitHub username is not listed, please submit an issue so we can get you added!
- Gremlin Query Language

View File

@ -11,20 +11,18 @@
function ready(){
var $ = document.querySelector.bind(document);
var gun = Gun(location.origin + '/gun').get('example/todo/data');
gun.not(function(){
return this.put({hello: "world!"}).key('example/todo/data');
}).on(function renderToDo(val){
gun.on(function renderToDo(val){
var todoHTML = '';
for(key in val) {
if(!val[key] || key == '_') continue;
todoHTML += '<li style="width:400px;height:2em;">' + (val[key]||'').toString().replace(/\</ig, '&lt;') +
'<button style="float:right;" onclick=removeToDo("'+key+'")>X</button></li>';
for(field in val) {
if(!val[field] || field == '_') continue;
todoHTML += '<li style="width:400px;height:2em;">'
+ (val[field]||'').toString().replace(/\</ig, '&lt;')
+ '<button style="float:right;" onclick=removeToDo("'+field+'")>X</button></li>';
}
$("#todos").innerHTML = todoHTML;
});
$("#addToDo").onsubmit = function(){
var id = randomId();
gun.path(id).put(($("#todoItem").value||'').toString().replace(/\</ig, '&lt;'));
gun.set(($("#todoItem").value||'').toString().replace(/\</ig, '&lt;'));
$("#todoItem").value = "";
return false;
};

20
gun.js
View File

@ -380,6 +380,7 @@
}, gun._.at('null').emit({key: ctx.key, GET: 'NULL'});
}
var dat = ctx.data = {};
if(Gun.obj.empty(data)){ return cb.call(gun, null, data) }
if(!Gun.is.graph(data, function(node, soul, map){
if(err = Gun.union(gun, node).err){ return cb.call(gun, err, data) }
/*dat[soul] = Gun.union.pseudo(soul); map(function(val, field){
@ -537,10 +538,8 @@
ctx[$.soul + $.field] = true; // TODO: unregister instead?
return cb.call(gun, node[$.field], $.field || $.at);
}
console.log("VAL VAL VAL VAL VAL", delta, gun.__.meta($.soul));
if(ctx[$.soul] || !gun.__.meta($.soul).end){ return } // TODO: Add opt to change number of terminations.
ctx[$.soul] = true; // TODO: unregister instead?
console.log("CTX", ctx); // MARK COME BACK HERE!!! TODO!!!
if(ctx[$.soul] || ($.key && ctx[$.key]) || !gun.__.meta($.soul).end){ return } // TODO: Add opt to change number of terminations.
ctx[$.soul] = ctx[$.key] = true; // TODO: unregister instead?
cb.call(gun, Gun.obj.copy(node), $.field || $.at);
}, {raw: true});
@ -555,7 +554,9 @@
gun._.at('soul').event(function($){ // TODO: once per soul on graph. (?)
if(ctx[$.soul]){
ctx[$.soul](gun.__.graph[$.soul], $);
if(opt.raw){
ctx[$.soul](gun.__.graph[$.soul], $); // TODO: we get duplicate ons, once here and once from HAM.
}
} else {
(ctx[$.soul] = function(delta, $$){
var $$ = $$ || $, node = gun.__.graph[$$.soul];
@ -684,6 +685,7 @@
gun._.at('soul').emit({soul: s, field: null, from: soul, at: field, MAP: 'SOUL'});
} else {
if(opt.node){ return } // {node: true} maps over only sub nodes.
console.log("trigger next thing", field, val);
cb.call(gun, val, field);
gun._.at('soul').emit({soul: soul, field: field, MAP: 'SOUL'});
}
@ -991,6 +993,7 @@
tab.get = tab.get || function(key, cb, opt){
if(!key){ return }
cb = cb || function(){};
cb.GET = true;
(opt = opt || {}).url = opt.url || {};
opt.headers = Gun.obj.copy(tab.headers);
if(Gun.is.soul(key)){
@ -1003,7 +1006,8 @@
var path = (path = Gun.is.soul(key))? tab.prefix + tab.prenode + path
: tab.prefix + tab.prekey + key, node = store.get(path), graph, soul;
if(Gun.is.node(node)){
(graph = {})[soul = Gun.is.soul.on(node)] = node;
(cb.graph = cb.graph || {}
)[soul = Gun.is.soul.on(node)] = (graph = {})[soul] = cb.node = node;
cb(null, graph);
(graph = {})[soul] = Gun.union.pseudo(soul); // end.
return cb(null, graph);
@ -1019,13 +1023,13 @@
if(!p.graph && !Gun.obj.empty(cb.graph)){ // if we have local data
tab.put(p.graph = cb.graph, function(e,r){ // then sync it if we haven't already
Gun.log("Stateless handshake sync:", e, r);
}, {peers: tab.peers(url)}); // to the peer.
}, {peers: tab.peers(url)}); // to the peer. // TODO: This forces local to flush again, not necessary.
// TODO: What about syncing our keys up?
}
Gun.is.graph(reply.body, function(node, soul){ // make sure for each received node
if(!Gun.is.soul(key)){ tab.key(key, soul, function(){}, {local: true}) } // that the key points to it.
});
setTimeout(function(){ tab.put(reply.body, function(){}, {local: true}) },1); // and flush the in memory nodes of this graph to localStorage after we've had a chacne to union on it.
setTimeout(function(){ tab.put(reply.body, function(){}, {local: true}) },1); // and flush the in memory nodes of this graph to localStorage after we've had a chance to union on it.
}), opt);
cb.peers = true;
});

View File

@ -9,7 +9,7 @@ Gun.on('opt').event(function(gun, opts) {
if ((opts.file === false) || (opts.s3 && opts.s3.key)) {
return; // don't use this plugin if S3 is being used.
}
console.log("WARNING! This `file.js` module for gun is intended only for local development testing!")
opts.file = opts.file || 'data.json';
var fs = require('fs');
file.raw = file.raw || (fs.existsSync || require('path').existsSync)(opts.file) ? fs.readFileSync(opts.file).toString() : null;
@ -23,15 +23,18 @@ Gun.on('opt').event(function(gun, opts) {
get: function get(key, cb, o){
var graph, soul;
if(soul = Gun.is.soul(key)){
(graph = {})[soul] = all.nodes[soul];
cb(null, graph);
(graph = {})[soul] = Gun.union.pseudo(soul); // end.
return cb(null, graph);
if(all.nodes[soul]){
(graph = {})[soul] = all.nodes[soul];
cb(null, graph);
(graph = {})[soul] = Gun.union.pseudo(soul);
cb(null, graph); // end.
}
return;
}
Gun.obj.map(all.keys[key], function(rel){
if(Gun.is.soul(rel)){ get(rel, cb, o) }
if(Gun.is.soul(rel)){ get(soul = rel, cb, o) }
});
cb(null, {});
return soul? cb(null, {}) : cb(null, null);
},
put: function(graph, cb, o){
all.nodes = gun.__.graph;

View File

@ -15,22 +15,52 @@
s3.get = s3.get || function(key, cb, opt){
if(!key){ return }
cb = cb || function(){};
opt = opt || {};
(opt = opt || {}).ctx = opt.ctx || {};
opt.ctx.load = opt.ctx.load || {};
if(key[Gun._.soul]){
key = s3.prefix + s3.prenode + key[Gun._.soul];
key = s3.prefix + s3.prenode + Gun.is.soul(key);
} else {
key = s3.prefix + s3.prekey + key;
}
s3.GET(key, function(err, data, text, meta){
Gun.log('via s3', key, err);
if(meta && meta[Gun._.soul]){
return s3.get(meta, cb); // SUPER SUPER IMPORTANT TODO!!!! Make this get via GUN in case soul is already cached!
// HUGE HUGE HUGE performance gains could come from the above line being updated! (not that this module is performant).
}
if(err && err.statusCode == 404){
err = null; // we want a difference between 'unfound' (data is null) and 'error' (auth is wrong).
}
cb(err, data);
// TODO: optimize KEY command to not write data if there is only one soul (which is common).
if(meta && (meta.key || meta[Gun._.soul])){
if(err){ return cb(err) }
if(meta.key && Gun.obj.is(data) && !Gun.is.node(data)){
return Gun.obj.map(data, function(rel, soul){
if(!(soul = Gun.is.soul(rel))){ return }
opt.ctx.load[soul] = false;
s3.get(rel, cb, {next: 's3', ctx: opt.ctx}); // TODO: way faster if you use cache.
});
}
if(meta[Gun._.soul]){
return s3.get(meta, cb); // TODO: way faster if you use cache.
}
return cb({err: Gun.log('Cannot determine S3 key data!')});
}
if(data){
meta.soul = Gun.is.soul.on(data);
if(!meta.soul){
err = {err: Gun.log('No soul on node S3 data!')};
}
} else {
return cb(err, null);
}
if(err){ return cb(err) }
opt.ctx.load[meta.soul] = true;
var graph = {};
graph[meta.soul] = data;
cb(null, graph);
(graph = {})[meta.soul] = Gun.union.pseudo(meta.soul);
cb(null, graph);
if(Gun.obj.map(opt.ctx.load, function(loaded, soul){
if(!loaded){ return true }
})){ return } // return IF we have nodes still loading.
cb(null, {});
});
}
s3.put = s3.put || function(nodes, cb){
@ -90,19 +120,25 @@
s3.wait = s3.wait || null;
s3.key = s3.key || function(key, soul, cb){
var meta = {};
meta[Gun._.soul] = soul = Gun.text.is(soul)? soul : (soul||{})[Gun._.soul];
if(!key){
return cb({err: "No key!"});
}
if(!soul){
return cb({err: "No soul!"});
}
s3.PUT(s3.prefix + s3.prekey + key, '', function(err, reply){ // key is 2 bytes??? Should be smaller. Wait HUH? What did I mean by this?
Gun.log("s3 put reply", soul, err, reply);
if(err || !reply){
s3.key(key, soul, cb); // naive implementation of retry TODO: BUG: need backoff and anti-infinite-loop!
return;
}
cb();
}, {Metadata: meta});
var path = s3.prefix + s3.prekey + key, meta = {key: '0.2'}, rel = {};
meta[Gun._.soul] = rel[Gun._.soul] = soul = Gun.is.soul(soul) || soul;
s3.GET(path, function(err, data, text, _){
var souls = data || {};
souls[soul] = rel;
s3.PUT(path, souls, function(err, reply){
Gun.log("s3 key reply", soul, err, reply);
if(err || !reply){
return s3.key(key, soul, cb); // naive implementation of retry TODO: BUG: need backoff and anti-infinite-loop!
}
cb();
}, {Metadata: meta});
});
}
opt.hooks = opt.hooks || {};

View File

@ -115,10 +115,22 @@
key[Gun._.soul] = req.url.query[Gun._.soul];
}
console.log("tran.get", key);
gun.get(key, function(err, node){
//tran.sub.scribe(req.tab, node._[Gun._.soul]);
cb({headers: reply.headers, chunk: (err? (err.err? err : {err: err || "Unknown error."}) : node || null)});
cb({headers: reply.headers, body: {_: {'#': Gun.is.soul.on(node) }} }); // TODO: symbol shouldn't be hard coded!
gun.get(key, function(err, graph){
//tran.sub.scribe(req.tab, graph._[Gun._.soul]);
console.log("tran.get", key, "<---", err, graph);
if(err || !graph){
return cb({headers: reply.headers, body: (err? (err.err? err : {err: err || "Unknown error."}) : null)});
}
if(Gun.obj.empty(graph)){ return cb({headers: reply.headers, body: graph}) } // we're out of stuff!
// TODO: chunk the graph even if it is already chunked. pseudo code below!
/*Gun.is.graph(graph, function(node, soul){
if(Object.keys(node).length > 100){
// split object into many objects that have a fixed size
// iterate over each object
// cb({headers: reply.headers, chunk: {object} );
}
});*/
return cb({headers: reply.headers, chunk: graph }); // keep streaming
});
}
tran.put = function(req, cb){
@ -130,7 +142,7 @@
if(tran.put.key(req, cb)){ return }
// some NEW code that should get revised.
if(Gun.is.node(req.body) || Gun.is.graph(req.body)){
console.log("tran.put", 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 = {};

View File

@ -1,165 +0,0 @@
var expect = global.expect = require("./expect");
var Gun = Gun || require('../gun');
Gun.log.squelch = true;
describe('All', function(){
var gun = Gun(), g = function(){
return Gun({hooks: {get: ctx.get}});
}, ctx = {};
/*
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, null), gun._.at('null').emit() }
if(ctx.soul = Gun.is.soul.on(data)){
gun._.at('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._.at('node').emit({soul: ctx.soul});
}, opt);
*/
it('prep hook', function(done){
var peer = Gun(), ref;
ctx.get = function(key, cb){
var c = 0;
cb = cb || function(){};
if('big' !== key){ return cb(null, null) }
setTimeout(function badNetwork(){
c += 1;
var data = {_: {'#': Gun.is.soul.on(ref), '>': {}}};
if(!ref['f' + c]){
return cb(null, data);
}
data._[Gun._.HAM]['f' + c] = ref._[Gun._.HAM]['f' + c];
data['f' + c] = ref['f' + c];
cb(null, data);
setTimeout(badNetwork, 5);
},5);
}
ctx.get.fake = {};
for(var i = 1; i < 6; i++){
ctx.get.fake['f'+i] = i;
}
var big = peer.put(ctx.get.fake).val(function(val){
ref = val;
ctx.get('big', function(err, data){
var next = Gun.obj.map(data, function(val, field){
if(Gun._.meta === field){ return }
return true;
});
//console.log(data);
if(!next){ done() }
});
gun.opt({hooks: {get: ctx.get}});
});
});
it('map chain', function(done){
var set = gun.put({a: {here: 'you'}, b: {go: 'dear'}, c: {sir: '!'} });
set.map().val(function(obj, field){
if(obj.here){
done.a = obj.here;
expect(obj.here).to.be('you');
}
if(obj.go){
done.b = obj.go;
expect(obj.go).to.be('dear');
}
if(obj.sir){
done.c = obj.sir;
expect(obj.sir).to.be('!');
}
if(done.a && done.b && done.c){
done();
}
});
});
it('map chain path', function(done){
var set = gun.put({
a: {name: "Mark",
pet: {coat: "tabby", name: "Hobbes"}
}, b: {name: "Alice",
pet: {coat: "calico", name: "Cali"}
}, c: {name: "Bob",
pet: {coat: "tux", name: "Casper"}
}
});
set.map().path('pet').val(function(obj, field){
if(obj.name === 'Hobbes'){
done.hobbes = obj.name;
expect(obj.name).to.be('Hobbes');
expect(obj.coat).to.be('tabby');
}
if(obj.name === 'Cali'){
done.cali = obj.name;
expect(obj.name).to.be('Cali');
expect(obj.coat).to.be('calico');
}
if(obj.name === 'Casper'){
done.casper = obj.name;
expect(obj.name).to.be('Casper');
expect(obj.coat).to.be('tux');
}
if(done.hobbes && done.cali && done.casper){
done();
}
});
});
it('get big on', function(done){
var c = 0;
g().get('big').on(function(val){
delete val._;
c += 1;
if(c === 1){
expect(val).to.eql({f1: 1});
}
if(c === 5){
expect(val).to.eql({f1: 1, f2: 2, f3: 3, f4: 4, f5: 5});
done();
}
});
});
it('get big on delta', function(done){
var c = 0;
g().get('big').on(function(val){
delete val._;
c += 1;
if(c === 1){
expect(val).to.eql({f1: 1});
}
if(c === 5){
expect(val).to.eql({f5: 5});
done();
}
}, true);
});
it('get val', function(done){
g().get('big').val(function(obj){
delete obj._;
expect(obj.f1).to.be(1);
expect(obj.f5).to.be(5);
done();
});
});
it('get big map val', function(done){
g().get('big').map().val(function(val, field){
delete val._;
if('f1' === field){
expect(val).to.be(1);
}
if('f5' === field){
expect(val).to.be(5);
done();
}
});
});
});

View File

@ -1630,15 +1630,14 @@ describe('Gun', function(){
gun.put({a: 1, z: -1}).key('pseudo');
gun.put({b: 2, z: 0}).key('pseudo');
Gun.log.verbose = true;
gun.get('pseudo').val(function(val){
expect(val.a).to.be(1);
expect(val.b).to.be(2);
expect(val.z).to.be(0);
//done();
done();
});
});
return;
it('get pseudo merge on', function(done){
var gun = Gun();
@ -1646,18 +1645,16 @@ describe('Gun', function(){
gun.put({b: 2, z: 0}).key('pseudon');
gun.get('pseudon').on(function(val){
console.log("HOW MANY pseudon TIMES??", val);
if(done.val){ return } // TODO: Maybe prevent repeat ons where there is no diff?
done.val = val;
expect(val.a).to.be(1);
expect(val.b).to.be(2);
expect(val.z).to.be(0);
//done();
done();
});
});
return;
it('get pseudo merge across peers', function(done){
alert(1);
Gun.on('opt').event(function(gun, o){
if(connect){ return }
gun.__.opt.hooks = {get: function(key, cb, opt){
@ -1718,5 +1715,164 @@ describe('Gun', function(){
},10);
},10);
});
});
describe('Streams', function(){
var gun = Gun(), g = function(){
return Gun({hooks: {get: ctx.get}});
}, ctx = {gen: 5, extra: 45, network: 2};
it('prep hook', function(done){
this.timeout(ctx.gen * ctx.extra);
var peer = Gun(), ref;
ctx.get = function(key, cb){
var c = 0;
cb = cb || function(){};
if('big' !== key){ return cb(null, null) }
setTimeout(function badNetwork(){
c += 1;
var soul = Gun.is.soul.on(ref);
var graph = {};
var data = graph[soul] = {_: {'#': soul, '>': {}}};
if(!ref['f' + c]){
return cb(null, graph), cb(null, {});
}
data._[Gun._.HAM]['f' + c] = ref._[Gun._.HAM]['f' + c];
data['f' + c] = ref['f' + c];
cb(null, graph);
setTimeout(badNetwork, ctx.network);
},ctx.network);
}
ctx.get.fake = {};
for(var i = 1; i < (ctx.gen) + 1; i++){
ctx.get.fake['f'+i] = i;
ctx.length = i;
}
var big = peer.put(ctx.get.fake).val(function(val){
ref = val;
ctx.get('big', function(err, graph){
if(Gun.obj.empty(graph)){ done() }
});
gun.opt({hooks: {get: ctx.get}});
});
});
it('map chain', function(done){
var set = gun.put({a: {here: 'you'}, b: {go: 'dear'}, c: {sir: '!'} });
set.map().val(function(obj, field){
if(obj.here){
done.a = obj.here;
expect(obj.here).to.be('you');
}
if(obj.go){
done.b = obj.go;
expect(obj.go).to.be('dear');
}
if(obj.sir){
done.c = obj.sir;
expect(obj.sir).to.be('!');
}
if(done.a && done.b && done.c){
done();
}
});
});
it('map chain path', function(done){
var set = gun.put({
a: {name: "Mark",
pet: {coat: "tabby", name: "Hobbes"}
}, b: {name: "Alice",
pet: {coat: "calico", name: "Cali"}
}, c: {name: "Bob",
pet: {coat: "tux", name: "Casper"}
}
});
set.map().path('pet').val(function(obj, field){
if(obj.name === 'Hobbes'){
done.hobbes = obj.name;
expect(obj.name).to.be('Hobbes');
expect(obj.coat).to.be('tabby');
}
if(obj.name === 'Cali'){
done.cali = obj.name;
expect(obj.name).to.be('Cali');
expect(obj.coat).to.be('calico');
}
if(obj.name === 'Casper'){
done.casper = obj.name;
expect(obj.name).to.be('Casper');
expect(obj.coat).to.be('tux');
}
if(done.hobbes && done.cali && done.casper){
done();
}
});
});
it('get big on', function(done){
this.timeout(ctx.gen * ctx.extra);
var test = {c: 0, last: 0};
g().get('big').on(function(val){
if(test.done){ return console.log("hey yo! you got duplication on your ons!"); }
delete val._;
if(val['f' + (test.last + 1)]){
test.c += 1;
test.last += 1;
}
var obj = {};
for(var i = 1; i < test.c + 1; i++){
obj['f'+i] = i;
}
expect(val).to.eql(obj);
if(test.c === ctx.length){
test.done = true;
done();
}
});
});
it('get big on delta', function(done){
this.timeout(ctx.gen * ctx.extra);
var test = {c: 0, seen: {}};
g().get('big').on(function(val){
delete val._;
if(test.seen['f' + test.c]){ return }
test.seen['f' + test.c] = true;
test.c += 1;
var obj = {};
obj['f' + test.c] = test.c;
expect(val).to.eql(obj);
if(test.c === ctx.length){
done();
}
}, true);
});
it('get val', function(done){
this.timeout(ctx.gen * ctx.extra);
g().get('big').val(function(obj){
delete obj._;
expect(obj.f1).to.be(1);
expect(obj['f' + ctx.length]).to.be(ctx.length);
expect(obj).to.be.eql(ctx.get.fake);
done();
});
});
it('get big map val', function(done){
this.timeout(ctx.gen * ctx.extra);
var test = {c: 0, seen: {}};
g().get('big').map().val(function(val, field){
if(test.seen[field]){ return }
test.seen[field] = true;
delete val._;
expect(field).to.be('f' + (test.c += 1));
expect(val).to.be(test.c);
if(test.c === ctx.length){
done();
}
});
});
});
});