From fa5096d0751c094bf3fa4dfc21fe31c4714cc18a Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Tue, 20 Jan 2015 08:02:40 -0700 Subject: [PATCH] fixed major bug, added tests. --- examples/.gitignore | 1 + examples/Procfile | 1 - examples/angular/index.html | 2 +- examples/examples.sh | 42 ---- examples/{all.js => express.js} | 0 examples/hello-world.js | 22 +- examples/http.js | 13 +- examples/{start.sh => install.sh} | 2 +- examples/package.json | 4 +- gun.js | 34 +-- lib/file.js | 15 +- lib/s3.js | 2 +- lib/wsp.js | 2 +- lib/xhr.js | 354 ------------------------------ test/common.js | 78 ++++--- 15 files changed, 108 insertions(+), 464 deletions(-) delete mode 100644 examples/Procfile delete mode 100644 examples/examples.sh rename examples/{all.js => express.js} (100%) rename examples/{start.sh => install.sh} (94%) delete mode 100644 lib/xhr.js diff --git a/examples/.gitignore b/examples/.gitignore index 7df76dc9..b1cc390c 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -1,2 +1,3 @@ node_modules/* npm-debug.log +*data.json diff --git a/examples/Procfile b/examples/Procfile deleted file mode 100644 index 52aa1424..00000000 --- a/examples/Procfile +++ /dev/null @@ -1 +0,0 @@ -web: node all.js \ No newline at end of file diff --git a/examples/angular/index.html b/examples/angular/index.html index 72e0240c..60584cd9 100644 --- a/examples/angular/index.html +++ b/examples/angular/index.html @@ -55,7 +55,7 @@ $scope.data = {}; $scope.$data = gun.load('example/angular/todo').blank(function(){ console.log("Initializing Data!") - gun.set({chicken: 'feet'}).key('example/angular/todo'); + gun.set({}).key('example/angular/todo'); }).on(function(data){ Gun.obj.map(data, function(val, field){ if(val === $scope.data[field]){ return } diff --git a/examples/examples.sh b/examples/examples.sh deleted file mode 100644 index a9b8e4c5..00000000 --- a/examples/examples.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash - -# curl -fsSL http://gunDB.io/example.sh | bash -c - -echo "" -echo "" - -echo "updating apt-get..." -sudo bash -c "apt-get update -qq -y < /dev/null" > /dev/null - -# git, wget, curl, build-essential -if [ -z "$(which git | grep git)" ] || [ -z "$(which wget | grep wget)" ] || [ -z "$(which curl | grep curl)" ] || [ -z "$(which gcc | grep gcc)" ] || [ -z "$(which rsync | grep rsync)" ] -then - echo "installing git, wget, curl, build-essential, rsync..." - sudo bash -c "apt-get install -qq -y git wget curl build-essential rsync < /dev/null" > /dev/null 2>/dev/null -fi - -# node -NODE_VER=v0.11.14 -CUR_NODE_VER=$(node -v 2>/dev/null) -if [ -n "$(which node | grep node)" ] && [ "${NODE_VER}" == "$(node -v 2>/dev/null)" ]; then -#if [ "${NODE_VER}" == "${CUR_NODE_VER}" ]; then - echo node ${NODE_VER} already installed -else - echo "installing node ${NODE_VER}..." - curl -fsSL "http://nodejs.org/dist/${NODE_VER}/node-${NODE_VER}-linux-x64.tar.gz" \ - -o "/tmp/node-${NODE_VER}-linux-x64.tar.gz" - pushd /tmp - tar xf /tmp/node-${NODE_VER}-linux-x64.tar.gz - rm node-${NODE_VER}-linux-x64/{LICENSE,ChangeLog,README.md} - sudo rsync -a /tmp/node-${NODE_VER}-linux-x64/ /usr/local/ - sudo chown -R $(whoami) /usr/local -fi - -mkdir -p ~/gun -cd ~/gun -npm install gun -cd ./node_modules/gun/examples -npm install . -node all.js - -echo "" \ No newline at end of file diff --git a/examples/all.js b/examples/express.js similarity index 100% rename from examples/all.js rename to examples/express.js diff --git a/examples/hello-world.js b/examples/hello-world.js index 052bd6f9..1337fff6 100644 --- a/examples/hello-world.js +++ b/examples/hello-world.js @@ -1,18 +1,18 @@ var Gun = require('gun'); var gun = Gun({ - s3: { - key: '', // AWS Access Key - secret: '', // AWS Secret Token - bucket: '' // The bucket you want to save into - } + s3: { + key: '', // AWS Access Key + secret: '', // AWS Secret Token + bucket: '' // The bucket you want to save into + } }); 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)); - }); +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/'); \ No newline at end of file +console.log('Server running at http://127.0.0.1:1337/'); diff --git a/examples/http.js b/examples/http.js index 432358a4..21590548 100644 --- a/examples/http.js +++ b/examples/http.js @@ -4,9 +4,18 @@ var http = require('http'); var Gun = require('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 + } }); -var server = gun.attach(http.createServer(gun.server)).listen(port); +var server = http.createServer(function(req, res){ + gun.server(req, res); +}); +gun.attach(server); +server.listen(port); console.log('Server started on port ' + port + ' with /gun'); diff --git a/examples/start.sh b/examples/install.sh similarity index 94% rename from examples/start.sh rename to examples/install.sh index 6268cbdc..3ce1b6ff 100644 --- a/examples/start.sh +++ b/examples/install.sh @@ -25,4 +25,4 @@ cd ./node_modules/gun/examples npm install . -sudo /usr/local/bin/node ./all.js +sudo /usr/local/bin/node ./express.js diff --git a/examples/package.json b/examples/package.json index 853e280d..09df0a00 100644 --- a/examples/package.json +++ b/examples/package.json @@ -1,6 +1,6 @@ { "name": "examples", - "main": "all.js", + "main": "express.js", "description": "Example gun apps" , "version": "0.0.1" , "engines": { @@ -11,7 +11,7 @@ "gun": "~>0.0.9a" } , "scripts": { - "start": "node all.js", + "start": "node express.js", "test": "mocha" } } diff --git a/gun.js b/gun.js index 8c1a6f6a..8816cdc2 100644 --- a/gun.js +++ b/gun.js @@ -322,7 +322,6 @@ var gun = this.chain(); path = (path || '').split('.'); gun.back.shot.then(function trace(node){ // should handle blank and err! Err already handled? - console.log("path happening"); // MARK COME BACK HERE!!! gun.field = null; gun._.node = node; if(!path.length){ // if the path resolves to another node, we finish here. @@ -331,13 +330,14 @@ 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(val); // internals use this thus not frozen yet, but primitive values are passed as copies anyways in js. + gun.shot('then').fire(node, field); // js copies primitive values, thus we must pass by reference. } }); if(gun.back && gun.back._ && gun.back._.loaded){ // is this causing a double trigger? @@ -349,11 +349,10 @@ } Chain.get = function(cb){ var gun = this; - gun.shot.then(function(val){ - console.log("get happening", val); - if(!gun._.node){ return } // I think this is safe? + gun.shot.then(function(node, field){ cb = cb || function(){}; - cb.call(gun, Gun.obj.is(val)? Gun.obj.copy(val) : val); // frozen copy + cb.call(gun, field? node[field] : Gun.obj.copy(node)); // frozen copy + // TODO! BUG! Maybe? Should a field that is null trigger a blank instead? }); return gun; } @@ -372,6 +371,7 @@ 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 + // TODO: BUG! Maybe? Interesting, if delta is an object, I might have to .load! } }) }) @@ -404,13 +404,13 @@ // 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! + console.log("what do you want to set?", val); 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 } set = Gun.ify.state(set.nodes, Gun.time.is()); // set time state on nodes? 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 - console.log(cb.root); 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? if(Gun.fns.is(gun.__.opt.hooks.set)){ @@ -418,7 +418,9 @@ if(err){ return cb(err) } return cb(null); }); - }); + } else { + Gun.log.call(gun, "Warning! You have no persistence layer to save to!"); + } }); if(!gun.back){ gun.shot('then').fire(set); @@ -447,16 +449,20 @@ gun.shot.then(function(val){ cb = cb || function(){}; opt = opt || {}; - Gun.obj.map(val, function(obj, soul){ // by default it only maps over nodes - 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? - cb.call(this, obj, soul); - }); + Gun.obj.map(val, function(val, field){ // by default it only maps over nodes + if(Gun._.meta == field){ return } + if(Gun.is.soul(val)){ + gun.load(val).get(function(val){ // should map have support for blank? + cb.call(this, val, field); + }); + } else { + if(!opt.all){ return } // {all: true} maps over everything + cb.call(this, val, field); + } }); }); return gun; } - */ // 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. // Meaning it is more low level, such that even set uses union internally. diff --git a/lib/file.js b/lib/file.js index e5fab8d8..aa1ac83a 100644 --- a/lib/file.js +++ b/lib/file.js @@ -6,13 +6,12 @@ var Gun = require('../gun'), file = {}; 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. + if(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.json').toString() - : null; - var all = file.all = file.all || Gun.obj.ify(raw || {nodes: {}, keys: {}}); + opts.file = opts.file || 'data.json'; + var fs = require('fs'); + file.raw = file.raw || fs.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: { load: function(key, cb, options){ @@ -30,11 +29,11 @@ Gun.on('opt').event(function(gun, opts){ } } } - fs.writeFile(opts.file || 'data.json', Gun.text.ify(all), cb); + fs.writeFile(opts.file, Gun.text.ify(all), cb); } ,key: function(key, soul, cb){ all.keys[key] = soul; - fs.writeFile(opts.file || 'data.json', Gun.text.ify(all), cb); + fs.writeFile(opts.file, Gun.text.ify(all), cb); } }}, true); diff --git a/lib/s3.js b/lib/s3.js index 700b7d25..03526c98 100644 --- a/lib/s3.js +++ b/lib/s3.js @@ -3,7 +3,7 @@ var S3 = require('./aws'); Gun.on('opt').event(function(gun, opt){ - if(!opt || !opt.s3){ return } // don't use S3 if it isn't specified. + if(!opt.s3){ return } // don't use S3 if it isn't specified. opt.s3 = opt.s3 || {}; var s3 = gun.__.opt.s3 = gun.__.opt.s3 || S3(opt && opt.s3); s3.prefix = s3.prefix || opt.s3.prefix || ''; diff --git a/lib/wsp.js b/lib/wsp.js index 87299a60..b9c631eb 100644 --- a/lib/wsp.js +++ b/lib/wsp.js @@ -107,7 +107,7 @@ key = {}; key[Gun._.soul] = req.url.query[Gun._.soul]; } - console.log("transport.loading key ->", key, gun.__.graph, gun.__.keys); + //console.log("transport.loading key ->", key, gun.__.graph, gun.__.keys); gun.load(key, function(err, node){ //tran.sub.scribe(req.tab, node._[Gun._.soul]); cb({headers: reply.headers, body: (err? (err.err? err : {err: err || "Unknown error."}) : node || null)}); diff --git a/lib/xhr.js b/lib/xhr.js deleted file mode 100644 index 16e9e843..00000000 --- a/lib/xhr.js +++ /dev/null @@ -1,354 +0,0 @@ -;(function(){ - var Gun = require('../gun') - , formidable = require('formidable') - , url = require('url') - , meta = {}; - Gun.on('opt').event(function(gun, opt){ - gun.server = gun.server || function(req, res, next){ // this whole function needs refactoring and modularization - //console.log("\n\n GUN SERVER!"); - next = next || function(){}; - if(!req || !res){ return next() } - if(!req.url){ return next() } - if(!req.method){ return next() } - var msg = {}; - msg.url = url.parse(req.url, true); - if(!gun.server.regex.test(msg.url.pathname)){ return next() } - msg.url.key = msg.url.pathname.replace(gun.server.regex,'') || ''; - if(msg.url.key.toLowerCase() === '.js'){ - res.writeHead(200, {'Content-Type': 'text/javascript'}); - res.end(gun.server.js = gun.server.js || require('fs').readFileSync(__dirname + '/gun.js')); // gun server is caching the gun library for the client - return; - } - msg.url.key = msg.url.key.replace(/^\//i,'') || ''; // strip the base slash - msg.method = (req.method||'').toLowerCase(); - msg.headers = req.headers; - var body - , form = new formidable.IncomingForm() - , post = function(err, body){ - msg.body = body; - gun.__.opt.hooks.transport(msg, function(reply){ - if(!res){ return } - if(!reply){ return res.end() } - if(reply.headers){ - if(!res._headerSent){ - Gun.obj.map(reply.headers, function(val, field){ - res.setHeader(field, val); - }); - } - } - meta.CORS(req, res); // add option to disable this - if(Gun.obj.has(reply,'chunk')){ - res.write(Gun.text.ify(reply.chunk) || ''); - } - if(Gun.obj.has(reply,'body')){ - res.end(Gun.text.ify(reply.body) || ''); - } - }); - } - form.on('field',function(k,v){ - (body = body || {})[k] = v; - }).on('file',function(k,v){ - return; // files not supported in gun yet - }).on('error',function(e){ - if(form.done){ return } - post(e); - }).on('end', function(){ - if(form.done){ return } - post(null, body); - }); - form.parse(req); - } - gun.server.regex = /^\/gun/i; - if(!gun.__.opt.keepMaxSockets){ require('https').globalAgent.maxSockets = require('http').globalAgent.maxSockets = Infinity } // WARNING: Document this! - - gun.server.transport = (function(){ - function tran(req, cb){ - //console.log(req); - req.sub = req.headers['gun-sub']; // grab the sub - req.tab = tran.sub.s[req.sub] || {}; // check to see if there already is a tab associated with it, or create one - req.tab.sub = req.sub = req.sub || Gun.text.random(); // Generate a session id if we don't already have one - req.tran = tran.xhr(req, cb) || tran.jsonp(req, cb); // polyfill transport layer - clearTimeout(req.tab.timeout); - // raw test for now, no auth: - if(!req.tran){ return cb({headers: {"Content-Type": tran.json}, body: {err: "No transport layer!"}}) } - if('post' === req.method || 'patch' === req.method){ return tran.post(req, req.tran) } // TODO: Handle JSONP emulated POST via GET - if('get' !== req.method){ return req.tran({body: {err: "Invalid method"}}) } - if(!req.url.key){ return tran.sub(req, req.tran) } // get acts as sub, too. - return tran.load(req, req.tran); // else load the state for the tab! - } - tran.load = function(req, cb){ - var reply = {}, key; - reply.headers = {'Content-Type': tran.json}; - reply.headers['Gun-Sub'] = req.tab.sub = req.sub; - key = (Gun._.meta == req.url.key)? req.url.query : req.url.key; - console.log("Loading", req.url.key, 'for', req.tab); - gun.load(key, function(node){ - tran.sub.scribe(req.tab, node._[Gun._.soul]); - cb({ - headers: reply.headers - ,body: node - }); - }).blank(function(){ - cb({ - headers: reply.headers - ,body: null - }); - }).dud(function(err){ - cb({ - headers: reply.headers - ,body: {err: err || "Unknown error."} - }); - }); - } - tran.post = function(req, cb){ // post is used as patch, sad that patch has such poor support - if(!req.body){ return cb({body: {err: "No body"}}) } - if(req.url.key && Gun.obj.has(req.body, Gun._.soul)){ // key hook! - req.tab = tran.sub.s[req.sub] || {}; - req.tab.sub = req.sub; - console.log("key.hook", req.tab); - tran.sub.scribe(req.tab, req.body[Gun._.soul]); - return gun.load(req.body).get(function(frozen){ - // NEED to allow a security callback so server can tamper with this! - this.key(req.url.key, function(err, reply){ - reply = reply || {}; - if(err){ reply.err = err } - reply = {body: reply}; - reply.headers = {'Content-Type': tran.json}; - reply.headers['Gun-Sub'] = req.tab.sub; - cb(reply); - }); - }); // do I need to handle the blank case? :/ Not sure. - } - // raw test for now, no auth: - // should probably load all the nodes first? YES. - var context = gun.union(req.body, function(err, context){ // data safely transformed - cb = cb || function(){}; - if(err || context.err){ return cb({body: {err: context.err}}) } - if(Gun.fns.is(gun.__.opt.hooks.set)){ - gun.__.opt.hooks.set(context.nodes, function saved(err, data){ // now iterate through those nodes to S3 and get a callback once all are saved - var body = {}; - if(err){ - body.err = err ; - } - if(!req.sub){ - if(!err){ - body = defer.map({}, context.nodes, 1); - } - return cb({body: body}); - } - var now = tran.post.s[req.sub]; // begin our stupid Chrome fix, we should abstract this out into defer (where it belogns) to keep things clean. - if(!now){ return } // utoh we've lost our reply to the tab! - clearTimeout(now.timeout); - now.body = now.body || {}; // make sure we have a body for our multi-response in a single response. - if(req.wait){ // did this request get deferred? - (now.body.refed = now.body.refed || {})[req.wait] = err? {err: err} : defer.map({}, context.nodes, 1); // then reply to it "here". - } else { - now.body.reply = err? {err: err} : defer.map({}, context.nodes, 1); // else this is the original POST that had to be upgraded. - } - if(0 < (now.count = ((now.count || 0) - 1))){ - // Don't reply till all deferred POSTs have successfully heard back from S3. (Sarcasm: Like counting guarantees that) - return now.timeout = setTimeout(saved, gun.__.opt.throttle * 2 * 1000); // reply not guaranteed, so time it out, in seconds. - } - if(Gun.fns.is(now)){ - now({body: now.body}); // FINALLY reply for ALL the POSTs for that session that accumulated. - } else { - // console.log("Error! We deleted our response!"); - } - Gun.obj.del(tran.post.s, req.sub); // clean up our memory. - // need to rewrite that if Stream is enabled that both Stream + State save are guaranteed before replying. - }); - // stuff past this point is just stupid implementation optimizations. - function defer(nodes, req){ // because Chrome can only handle 4 requests at a time, sad face. - if(!req.sub){ - return; - } - var next = tran.post.s[req.sub]; - if(!next){ // was there a previous POST? If not, we become the previous POST. - //cb({chunk: ''}); // because on some services (heroku) you need to reply starting a stream to keep the connection open. - return tran.post.s[req.sub] = cb; - } - next.count = (next.count || 1) + 1; // start counting how many we accumulate - next.body = next.body || {}; // this becomes the polyfill for all the posts - next.body.refed = next.body.refed || {}; // where we refeed the responses for the deferred POSTs. - req.wait = Gun.text.random(); // generate an random id for this deferred POST. - next.body.refed[req.wait] = false; // establish that we are incomplete. - cb({body: {defer: req.wait}}); // end this POST immediately so Chrome only ever uses a couple connections. - cb = null; // null it out so we don't accidentally reply to it once we hear back from S3. - } - defer.map = function(now, nodes, val){ // shortcut for maping which nodes were saved successfully - if(!now){ return } - Gun.obj.map(nodes, function(node, soul, map){ - now[soul] = val; - }); - return now; - } - defer(context.nodes, req); // actually do the weird stuff to make Chrome not be slow - } else { - context.err = "Warning! You have no persistence layer to save to!"; - Gun.log(context.err); - cb({body: {err: "Server has no persistence layer!"}}); - } - }); - if(context.err){ - cb({body: {err: context.err}}); - return cb = null; - } - Gun.obj.map(context.nodes, function(node, soul){ // live push the stream out in realtime to every tab subscribed - var msg = {}; - msg.headers = req.headers; // polyfill the delta as its own message. - msg.body = node; - console.log("emit delta", soul); - tran.push(soul).emit(msg); - }); - } - tran.post.s = {}; - tran.sub = function(req, cb){ - //console.log("<-- ", req.sub, req.tran ," -->"); - req.tab = tran.sub.s[req.sub]; - if(!req.tab){ - cb({ - headers: {'Gun-Sub': ''} - ,body: {err: "Please re-initialize sub."} - }); - return; - } - //console.log("\n\n\n THE CURRENT STATUS IS");console.log(req.tab); - if(req.tab.queue && req.tab.queue.length){ - tran.clean(req.tab); // We flush their data now, if they don't come back for more within timeout, we remove their session - console.log("_____ NOW PUSHING YOUR DATA ______", req.sub); - cb({ headers: {'Gun-Sub': req.sub} }); - while(1 < req.tab.queue.length){ - cb({ chunk: req.tab.queue.shift() }); - } - cb({ body: req.tab.queue.shift() }); - } else { - cb({chunk: ''}); // same thing as the defer code, initialize a stream to support some services (heroku). - req.tab.reply = cb; - console.log("_____ STANDING BY, WAITING FOR DATA ______", req.sub); - } - } - tran.sub.s = {}; - tran.clean = function(tab, mult){ - if(!tab){ return } - mult = mult || 1; - clearTimeout(tab.timeout); - tab.timeout = setTimeout(function(){ - if(!tab){ return } - if(tab.reply){ tab.reply({body: {err: "Connection timed out"}}) } - console.log("!!!! DISCONNECTING CLIENT !!!!!", tab.sub); - Gun.obj.del(tran.sub.s, tab.sub) - }, gun.__.opt.disconnect * mult * 1000); // in seconds - } - tran.sub.scribe = function(tab, soul){ - tran.sub.s[tab.sub] = tab; - tab.subs = tab.subs || {}; - console.log("meow subscribes", soul); - tab.subs[soul] = tab.subs[soul] || tran.push(soul).event(function(req){ - if(!req){ return } - if(!tab){ return this.off() } // resolve any dangling callbacks - req.sub = req.sub || req.headers['gun-sub']; - if(req.sub === tab.sub){ return } // do not send back to the tab that sent it - console.log('FROM:', req.sub, "TO:", tab.sub); - tran.clean(tab); - if(tab.reply){ - tab.reply({ - headers: {'Gun-Sub': tab.sub} - ,body: req.body - }); - tab.reply = null; - return; - } - (tab.queue = tab.queue || []).push(req.body); - }); - tran.clean(tab, 2); - } - tran.xhr = function(req, cb){ // Streaming Long Polling - return req.tran || (req.headers['x-requested-with'] === 'XMLHttpRequest'? transport : null); - function transport(res){ - if(!res){ return } - var reply = {headers: {}}; - if(res.headers){ - Gun.obj.map(res.headers, function(val, field){ - reply.headers[field] = val; - }); - } - reply.headers["Content-Type"] = tran.json; - if(Gun.obj.has(res,'chunk')){ - cb({ - headers: reply.headers - ,chunk: Gun.text.ify(res.chunk) + '\n' - }) - } - if(Gun.obj.has(res,'body')){ - cb({ - headers: reply.headers - ,body: Gun.text.ify(res.body) - }) - } - } - } - tran.jsonp = function(req, cb){ - var reply = {headers: {}}; - if(req.tran || req.headers['x-requested-with']){ return } - if((req.url.query||{}).jsonp){ - cb.jsonp = req.url.query.jsonp; - Gun.obj.del(req.url.query, 'jsonp'); - req.headers['x-requested-with'] = 'jsonp'; // polyfill - req.sub = req.headers['gun-sub'] = req.headers['gun-sub'] || req.url.query['Gun-Sub'] || req.url.query['gun-sub']; - Gun.obj.del(req.url.query, 'Gun-Sub'); - Gun.obj.del(req.url.query, 'gun-sub'); - return transport; - } - function transport(res){ - if(!res){ return } - if(res.headers){ - Gun.obj.map(res.headers, function(val, field){ - reply.headers[field] = val; - }); - } - if(Gun.obj.has(res,'chunk') && (!reply.body || Gun.list.is(reply.chunks))){ - (reply.chunks = reply.chunks || []).push(res.chunk); - } - if(Gun.obj.has(res,'body')){ - reply.body = res.body; // self-reference yourself so on the client we can get the headers and body. - reply.body = ';'+ cb.jsonp + '(' + Gun.text.ify(reply) + ');'; // javascriptify it! can't believe the client trusts us. - cb(reply); - } - } - } - tran.json = 'application/json'; - tran.push = Gun.on.create(); - return tran; - }()); - - opt.hooks = opt.hooks || {}; - gun.opt({hooks: { - transport: opt.hooks.transport || gun.server.transport - }}, true); - }); - meta.json = 'application/json'; - meta.JSON = function(res, data, multi){ - if(res && !res._headerSent){ - res.setHeader('Content-Type', meta.json); - } - if(!data && multi){ - res.write(Gun.text.ify(multi||'') + '\n'); - return; - } - return res.end(Gun.text.ify(data||'')); - }; - meta.CORS = function(req, res){ - if(!res || res.CORSHeader || res._headerSent){ return } - res.setHeader("Access-Control-Allow-Origin", "*"); - res.setHeader("Access-Control-Allow-Methods", ["POST", "GET", "PUT", "DELETE", "OPTIONS"]); - res.setHeader("Access-Control-Allow-Credentials", false); - res.setHeader("Access-Control-Max-Age", 1000 * 60 * 60 * 24); - res.setHeader("Access-Control-Allow-Headers", ["X-Requested-With", "X-HTTP-Method-Override", "Content-Type", "Accept", "Gun-Sub"]); - res.setHeader("Access-Control-Expose-Headers", ["Content-Type", "Gun-Sub"]); - res.CORSHeader = true; - if(req && req.method === 'OPTIONS'){ - res.end(); - return true; - } - }; -}()); diff --git a/test/common.js b/test/common.js index 72ce13f2..d93bbb24 100644 --- a/test/common.js +++ b/test/common.js @@ -1,7 +1,6 @@ describe('Gun', function(){ var Gun = require('../gun') , t = {}; - require(__dirname + '/../lib/file'); describe('Utility', function(){ describe('Type Check', function(){ it('binary', function(){ @@ -304,8 +303,6 @@ describe('Gun', function(){ data = {c: function(){}, d: 'hi'}; test = Gun.ify(data); expect(test.err.invalid).to.be.ok(); - - console.log(test.nodes); }); it('union', function(){ @@ -317,32 +314,61 @@ describe('Gun', function(){ Gun.union(graph, prime); // TODO: BUG! Where is the expect??? }); - it('path', function(done){ - console.log("fix path!"); - return done(); // TODO: FIX! This is broken because of API changes, fix it! + describe('API', function(){ - 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); - done(); - }); - console.log("________________________"); - }); + var gun = Gun(); - it('set key get', function(done){ - var gun = require('gun/gun')(); - - gun.set({hello: "world"}).key('hello/world').get(function(val){ - console.log("?", val); + it('set key get', function(done){ + gun.set({hello: "world"}).key('hello/world').get(function(val){ expect(val.hello).to.be('world'); done(); - }).blank(function(){ console.log("nothing"); }) - .err(function(){ console.log("err"); }) - }); + }); + }); + /* + it('load', function(done){ + gun.load('hello/world').get(function(val){ + expect(val.hello).to.be('world'); + done(); + }); + }); + it('load path', function(done){ + gun.load('hello/world').path('hello').get(function(val){ + expect(val).to.be('world'); + done(); + }); + }); + + it('load set path', function(done){ + gun.load('hello/world').set({hello: 'Mark'}).path('hello').get(function(val){ + expect(val).to.be('Mark'); + done(); + }); + }); + */ + it('load path set', function(done){ + gun.load('hello/world').path('hello').set('World').get(function(val){ + expect(val).to.be('World'); + done(); + }); + }); + + 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); + done(); + }); + console.log("________________________"); + }); + + }); });