Hypothetical Amnesia Machine ready!

This commit is contained in:
Mark Nadal
2014-10-08 00:55:04 -07:00
parent 8d1ec10375
commit 82a9fe0fd0
11 changed files with 1123 additions and 836 deletions

View File

@@ -17,5 +17,6 @@ app.listen(port);
console.log('Express started on port ' + port + ' with /gun');
gun.load('blob/data').blank(function(){ // in case there is no data on this key
gun.set({ hello: "world", from: "Mark Nadal" }).key('blob/data'); // save some sample data
console.log("blankety blank");
gun.set({ hello: "world", from: "Mark Nadal",_:{'#':'0DFXd0ckJ9cXGczusNf1ovrE'}}).key('blob/data'); // save some sample data
});

View File

@@ -1,226 +0,0 @@
<!DOCTYPE html>
<html ng-app="admin">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="../../gun.js"></script>
</head>
<body ng-controller="editor">
<style>
html, body {
margin: 0;
padding: 0;
font-family: Verdana, Geneva, sans-serif;
}
a {
color: skyblue;
text-decoration: none;
cursor: poiner;
}
ul, li {
list-style-type: none;
}
ul:hover, li:hover {
list-style-type: inherit;
}
input {
border: none;
border-bottom: dashed 1px gainsboro;
}
.none {
display: none;
}
.fight {
z-index: 9999;
background: brown;
color: white;
position: absolute;
width: 100%;
height: 100%;
}
.player input, .player button {
font-size: 18pt;
}
.hold {
padding: .2em;
}
</style>
<div id="duel" class="none fight">
<center>
<h2>GET READY!</h2>
<button id="reset" class="none">
Reset the game!
</button>
</center>
</div>
<div class="hold">
<h2><i>GUNSLINGER</i></h2>
<span>Old West Duel! Two players are needed, whoever can tap the screen first to draw their pistol and shoot wins!</span><br>
<span><b>Fastest gun in the west, <span name="fastest">nut'n</span> seconds, by <span name="slinger">nobody</span>.</b></span>
<form id="p1" class="player" onsubmit="return false;">
Player 1: <input type="text" name="p1" placeholder="nickname"> <button type="submit">Join!</button>
</form>
<form id="p2" class="player" onsubmit="return false;">
Player 2: <input type="text" name="p2" placeholder="nickname"> <button type="submit">Join!</button>
</form>
<span><b>Last duel won by <span name="last">no one</span> in <span name="ended">0</span> seconds against <span name="loser">nobody</span>.</b></span>
</div>
<textarea id="debug" style="position: fixed; bottom: 0; width: 100%; height: 5em; z-index: 99999999999;"></textarea>
<script>
console._log = console.log;console.log = function(a,s,d,f){ var arg = arguments, s = '';
for(var i in arg){ var val = arg[i]; if(typeof val == 'string'){ s += val + ' ' } else { s += JSON.stringify(val) + ' ' } }; $("#debug").prepend(s + '\n\n') }
$(function(){
var me = window.me = {},
game = window.game = {},
//gun = window.gun = Gun([location.origin + '/gun'])
//gun = window.gun = Gun(['http://localhost:8888/' + 'gun'])
gun = window.gun = Gun(['https://gunjs.herokuapp.com/gun'])
.load('game/duel', function(data){
console.log(data);
$(document).on('submit', '.player', function(e){
e.preventDefault();
var nick = $(this).find('input').val(), id = this.id;
console.log(nick, me.player);
if(!nick || me.player){ return }
gun.path(id).get(function(val){
if(val){ return }
this.set(me.player = nick);
me.took = id;
});
})
Gun.on(data._[Gun.sym.id]).event(function(node, local){
if(!node){ return }
console.log("change!", node);
if(node.start){ game.schedule(node.start) }
if(node.dqed){ game.dq(node.dqed) }
if(node.sling){ game.sling(node.sling, local) }
Gun.obj.map(node, game.set);
if(node.p1 || node.p2){
gun.path('p1').get(function(p1){ // start game?
if(!p1){ return }
gun.path('p2').get(function(p2){
if(!p2){ return }
game.play(p2);
})
});
}
game.timeout(true);
});
Gun.obj.map(data, game.set);
game.timeout(20);
});
game.$duel = $("#duel");
game.$duelm = game.$duel.clone();
game.play = function(p2){
if(!me.player){ return }
game.$duel.removeClass("none");
if(me.player == p2){ return }
me.scheduled = (+new Date()) + Math.round(Math.random() * 2000 + 2700); // MIN is the right number, and MAX is the SUM of both numbers.
gun.path('start').set(me.scheduled);
}
game.schedule = function(at){
console.log(" ------------------ START", at);
if(me.started){ return }
me.started = true;
me.scheduled = at;
Gun.schedule(at, function(){
me.shoot = true;
if(me.dqed){ return }
game.$duel.css({background: 'lime'}).find('h2').text("FIRE!");
});
}
game.fire = function(){
console.log('bang', me);
if(!me.started || me.fired){ return }
me.fired = (+ new Date());
if(me.fired < me.scheduled){ // DQ
me.dqed = me.player;
gun.path('dqed').set(me.player);
alert('DQing you');
return;
}
gun.path('dqed').get(function(yes){
alert('dqed ' + yes);
if(yes){ return }
game.$duel.css({background: 'gold'}).find('h2').text("STOP!");
gun.path('sling').set(me.player);
me.time = (me.fired - me.scheduled) / 1000; // in seconds
});
}
game.sling = function(){
me.count = (me.count || 0) + 1;
if(me.count < 2){ return }
$('#reset').removeClass('none');
gun.path('sling').get(function(sling){
if(!sling){ return }
if(sling == me.player){
game.$duel.css({background: 'red'}).find('h2').text("YOU DIED!!!");
gun.path('loser').set(me.player);
return;
}
game.$duel.css({background: 'skyblue'}).find('h2').text("YOU WON!!!");
if(!me.time || me.fired < me.scheduled || me.time < 0){ return }
gun.path('last').set(me.player);
gun.path('ended').set(me.time);
gun.path('fastest').get(function(time){
if(time <= me.time){ return }
gun.path('fastest').set(me.time);
gun.path('slinger').set(me.player);
});
});
}
game.dq = function(who){
$('#reset').removeClass('none');
if(who == me.player){
return game.$duel.css({background: 'gray'}).find('h2').text("DISQUALIFIED!!!");
}
if(me.dqed){
return game.$duel.find('h2').text("BOTH DISQUALIFIED!");
}
game.$duel.css({background: 'skyblue'}).find('h2').text("YOU WON!!!");
}
game.clear = function(){
me = {};
game.$duel.replaceWith(game.$duel = game.$duelm.clone());
gun.path('p1').set("");
gun.path('p2').set("");
gun.path('sling').set(null);
gun.path('start').set(null);
gun.path('dqed').set(null);
game.$duel.addClass('none');
}
game.timeout = function(wait){
if(true === wait){ return clearTimeout(me.timeout) }
wait = wait || 15;
clearTimeout(me.timeout);
me.timeout = setTimeout(game.clear, wait * 1000);
}
game.set = function(val, name){
if(val == game.nothing){ return }
$("[name='" + name + "']").text(val).val(val);
Gun.on("duel-" + name).emit(val, name);
}
game.plock = function(val, id){
$("#" + id).find('input').attr("readonly", val? true : false);
$("#" + id).find('button').text(val? "Taken!" : "Join!").attr("disabled", val? true : false);
}
Gun.on("duel-p1").event(game.plock);
Gun.on("duel-p2").event(game.plock);
$(document).on('click', '#duel', game.fire);
$(document).on('click', '#reset', game.clear);
}());
</script>
<script>
(function(){
return;
var game = Gun(location + 'gun').load('game/duel');
game.on('change').path('p1').or.path('p2').get(function(val){
$("#" + this.field).find('input').val(val? "Taken!" : "Join!").attr("disabled", val? true : false);
$("#" + this.field).find('button').val(val? "Taken!" : "Join!").attr("disabled", val? true : false);
});
}());
</script>
</body>
</html>

View File

@@ -62,15 +62,12 @@
var gun = Gun([location + 'gun']);
angular.module('admin', []).controller('editor', function($scope){
$scope.data = {};
$scope.$data = gun.load('blob/data', function(data){
$scope.data = data;
$scope.$apply();
Gun.on(data._[Gun.sym.id]).event(function(node){ // one liner-ify this!
Gun.obj.map(node, function(val, field){
$scope.data[field] = val;
});
$scope.$apply();
$scope.$data = gun.load('blob/data').get(function(data){
Gun.obj.map(data, function(val, field){
if(val === $scope.data[field]){ return }
$scope.data[field] = val;
});
$scope.$apply();
});
$scope.add = function(a,b,c){
$scope.$data.path($scope.field).set( $scope.data[$scope.field] = 'value' );

View File

@@ -9,7 +9,7 @@
, "dependencies": {
"express": "~>4.9.0",
"body-parser": "~>1.8.1",
"gun": "0.0.6-e"
"gun": "0.0.7"
}
, "scripts": {
"start": "node app.js",

195
examples/admin/slinger.html Normal file
View File

@@ -0,0 +1,195 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="../../gun.js"></script>
</head>
<body><center>
<style>
html, body {
font-family: Papyrus, fantasy;
font-size: 18pt;
}
.start .player {
color: white;
border: none;
padding: 1.5em;
background: skyblue;
font-family: Papyrus, fantasy;
}
.screen {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
}
.off {
display: none;
}
.white {
color: white;
}
</style>
<div class="start screen">
<!--<pre>
¦¦¦¦¦¦+ ¦¦+ ¦¦+ ¦¦¦+ ¦¦+ ¦¦¦¦¦¦¦+ ¦¦+ ¦¦+ ¦¦¦+ ¦¦+ ¦¦¦¦¦¦+ ¦¦¦¦¦¦¦+ ¦¦¦¦¦¦+
¦¦+----+ ¦¦¦ ¦¦¦ ¦¦¦¦+ ¦¦¦ ¦¦+----+ ¦¦¦ ¦¦¦ ¦¦¦¦+ ¦¦¦ ¦¦+----+ ¦¦+----+ ¦¦+--¦¦+
¦¦¦ ¦¦¦+ ¦¦¦ ¦¦¦ ¦¦+¦¦+ ¦¦¦ ¦¦¦¦¦¦¦+ ¦¦¦ ¦¦¦ ¦¦+¦¦+ ¦¦¦ ¦¦¦ ¦¦¦+ ¦¦¦¦¦+ ¦¦¦¦¦¦++
¦¦¦ ¦¦¦ ¦¦¦ ¦¦¦ ¦¦¦+¦¦+¦¦¦ +----¦¦¦ ¦¦¦ ¦¦¦ ¦¦¦+¦¦+¦¦¦ ¦¦¦ ¦¦¦ ¦¦+--+ ¦¦+--¦¦+
+¦¦¦¦¦¦++ +¦¦¦¦¦¦++ ¦¦¦ +¦¦¦¦¦ ¦¦¦¦¦¦¦¦ ¦¦¦¦¦¦¦+ ¦¦¦ ¦¦¦ +¦¦¦¦¦ +¦¦¦¦¦¦++ ¦¦¦¦¦¦¦+ ¦¦¦ ¦¦¦
+-----+ +-----+ +-+ +---+ +------+ +------+ +-+ +-+ +---+ +-----+ +------+ +-+ +-+
</pre>-->
<h1>GUN SLINGER</h1>
<h3>Select!</h3>
<button id="one" class="player">Player 1</button>
<button id="two" class="player">Player 2</button>
<h5>Next game available in 15 seconds or less...<h5>
</div>
<div class="shoot screen off white" style="background: tan;">
<h3>GET READY!</h3>
</div>
<div class="fire screen off" style="background: lime;">
<h3>FIRE!</h3>
<h5>by tapping this screen</h5>
</div>
<div class="stop screen off" style="background: yellow;">
<h3>STOP!</h3>
<h5>...waiting for the other player...</h5>
</div>
<div class="disqualified screen off white" style="background: red;">
<h3>DISQUALIFIED!</h3>
</div>
<div class="loser screen off white" style="background: red;">
<h3>YOU DIED!</h3>
</div>
<div class="draw screen off white" style="background: red;">
<h3>YOU BOTH DIED!</h3>
<button onclick="game.reset()">Reset Game</button>
</div>
<div class="winner screen off white" style="background: skyblue;">
<h3>YOU WON!</h3>
<button onclick="game.reset()">Reset Game</button>
</div>
<script>
$(function(){
var game = window.game = {me:{}}
//, gun = Gun(location.origin + '/gun')
, gun = window.gun = Gun('http://localhost:8888' + '/gun')
.load('game/duel')
;
gun.path('name').get(function(val){
console.log('gunslinger has results', val);
});
gun.get(function(player){
console.log("Game update", player);
if(game.timeout){
clearTimeout(game.timeout);
} else {
game.timeout = setTimeout(game.reset, 15 * 1000);
}
$('.start').find('#one').text(player.one? 'Taken!' : 'Player 1').attr('disabled', player.one? true : false);
$('.start').find('#two').text(player.two? 'Taken!' : 'Player 2').attr('disabled', player.two? true : false);
game.start(player);
game.compare(player);
});
gun.path('dqed').get(function(){
if(!game.me.player || game.me.dqed){ return }
game.me.default = true;
game.screen('winner');
});
gun.path('one').get(function(val){
console.log('gun player one', val);
if(null === val){
game.cancel();
}
})
game.start = function(player){
if(game.me.started){ return }
if(!player.one || !player.two){ return }
if(!player || !game.me || !game.me.player){ return }
console.log("start?", player, game.me);
if(player[game.me.player] == game.me[game.me.player]){
game.screen('shoot');
game.coordinate();
} else {
game.cancel();
}
}
gun.path('coordinate').get(game.coordinate = function(at){
if(!game.me.player){ return }
var started = game.me.started = Gun.roulette();
if(at){
Gun.schedule(at, function(){
if(game.me.dqed || game.me.default || started != game.me.started){ return }
game.screen('fire');
game.me.draw = (+new Date());
});
return;
}
if(game.me.player != 'two'){ return }
game.me.coordinate = (+new Date()) + Math.round(Math.random() * 2000 + 2700); // MIN is the right number, and MAX is the SUM of both numbers.
gun.path('coordinate').set(game.me.coordinate);
});
game.compare = function(score){
if(!game.me.player || !score.onespeed || !score.twospeed){ return }
if(score.onespeed < score.twospeed){
if(game.me.one){
game.screen('winner');
} else {
game.screen('loser');
}
} else
if(score.twospeed < score.onespeed){
if(game.me.two){
game.screen('winner');
} else {
game.screen('loser');
}
} else {
game.screen('draw');
}
}
game.reset = function(){
console.log("Resetting game");
gun.path('one').set(null);
gun.path('two').set(null);
gun.path('dqed').set(null);
gun.path('coordinate').set(null);
gun.path('onespeed').set(null);
gun.path('twospeed').set(null);
game.cancel();
}
game.cancel = function(){
game.screen();
game.me = {};
}
game.screen = function(screen){
$('.screen').addClass('off').filter('.' + (screen || 'start')).removeClass('off');
}
$('.player').on('click', function(){
if(game.me.player){ return }
game.me = {};
gun.path(game.me.player = this.id).set(game.me[game.me.player] = Gun.roulette());
});
$('.shoot').on('click', function(){
if(!game.me.player){ return }
game.me.dqed = true;
game.screen('disqualified');
gun.path('dqed').set(game.me[game.me.player]);
});
$('.fire').on('click', function(){
if(!game.me.player || game.me.fired){ return }
game.me.fired = (+new Date());
game.me.speed = game.me.fired - game.me.draw;
gun.path(game.me.player + 'speed').set(game.me.speed);
game.screen('stop');
});
})
</script>
</center></body>
</html>

View File

@@ -5,7 +5,7 @@
return new s3(opt);
}
var s = this;
s.own = a.on.split();
s.on = a.on.create();
s.mime = require('mime');
s.AWS = require('aws-sdk');
s.config = {};
@@ -33,7 +33,7 @@
s3.chain = s3.prototype;
s3.chain.put = function(key, o, cb, m){
if(!key){ return }
var m = m || {}
m = m || {}
m.Bucket = m.Bucket || this.config.bucket;
m.Key = m.Key || key;
if(a.obj.is(o) || a.list.is(o)){
@@ -56,7 +56,7 @@
Bucket: s.config.bucket
,Key: key
}, id = s3.id(m);
s.own.on(id).once(function(e,d,t,m,r){
s.on(id).once(function(e,d,t,m,r){
delete s.batch[id];
if(!a.fns.is(cb)){ return }
try{ cb(e,d,t,m,r);
@@ -69,15 +69,16 @@
s.batch[id] = (s.batch[id] || 0) + 1;
console.log("no batch!");
s.S3().getObject(m, function(e,r){
var d, t, m, r = r || (this && this.httpResponse);
if(e || !r){ return s.own.on(id).emit(e) }
var d, t, m;
r = r || (this && this.httpResponse);
if(e || !r){ return s.on(id).emit(e) }
r.Text = r.text = t = (r.Body||r.body||'').toString('utf8');
r.Type = r.type = r.ContentType || (r.headers||{})['content-type'];
if(r.type && 'json' === s.mime.extension(r.type)){
d = a.obj.ify(t);
}
m = r.Metadata;
s.own.on(id).emit(e, d, t, m, r); // Warning about the r parameter, is is the raw response and may result in stupid SAX errors.
s.on(id).emit(e, d, t, m, r); // Warning about the r parameter, is is the raw response and may result in stupid SAX errors.
});
return s;
}

1094
gun.js

File diff suppressed because it is too large Load Diff

46
on.js Normal file
View File

@@ -0,0 +1,46 @@
;(function(){
var setImmediate = setImmediate || function(cb){setTimeout(cb,0)}
function On(){
var chain = new Chain();
return chain.$ = function(where){
chain.$[where] = function(fn){
chain.$[where] = fn;
}
chain.where = where;
return chain;
}
}
On.is = function(On){ return (On instanceof On)? true : false }
function Chain(){
if(!(this instanceof Chain)){
return new Chain();
}
}
Chain.chain = Chain.prototype;
Chain.chain.emit = function(a,s,d,f){
var me = this
, where = me.where
, args = Array.prototype.slice.call(arguments);
setImmediate(function(){
if(!me || !me.$ || !me.$[where]){ return }
me.$[where].apply(me, args);
});
return me;
}
if(typeof window !== "undefined"){
window.On = On;
} else {
module.exports = On;
}
;(function(){ // test
var doSomething = function(){
var cb = On();
cb('now').emit(1,2,3);
return cb;
}
doSomething('foo', 'bar').now(function(a,b,c){
console.log("Oh yeah baby", a,b,c);
})
}());
}());

View File

@@ -1,5 +1,5 @@
{ "name": "gun"
, "version": "0.0.6-e"
, "version": "0.0.7"
, "author": "Mark Nadal"
, "description": "Graph engine."
, "engines": {

215
shots.js
View File

@@ -38,10 +38,10 @@
}
}
meta.CORS(req, res); // add option to disable this
if(reply.chunk){
if(Gun.obj.has(reply,'chunk')){
res.write(Gun.text.ify(reply.chunk) || '');
}
if(reply.body){
if(Gun.obj.has(reply,'body')){
res.end(Gun.text.ify(reply.body) || '');
}
});
@@ -71,17 +71,18 @@
if(!gun.__.opt.keepMaxSockets){ require('https').globalAgent.maxSockets = require('http').globalAgent.maxSockets = Infinity } // WARNING: Document this!
s3.load = s3.load || function(key, cb, opt){
if(!key){ return }
cb = cb || function(){};
opt = opt || {};
if(opt.id){
key = s3.prefix + s3.prenode + key;
if(key[Gun._.soul]){
key = s3.prefix + s3.prenode + key[Gun._.soul];
} else {
key = s3.prefix + s3.prekey + key;
}
s3.get(key, function(err, data, text, meta){
console.log('via s3', key, err);
if(meta && (key = meta[Gun.sym.id])){
return s3.load(key, cb, {id: true});
if(meta && meta[Gun._.soul]){
return s3.load(meta, cb);
}
if(err && err.statusCode == 404){
err = null; // we want a difference between 'unfound' (data is null) and 'error' (auth is wrong).
@@ -96,16 +97,16 @@
var next = s3.next
, ack = Gun.text.random(8)
, batch = s3.batch[next] = s3.batch[next] || {};
s3.event.on(ack).once(cb);
Gun.obj.map(nodes, function(node, id){
s3.on(ack).once(cb);
Gun.obj.map(nodes, function(node, soul){
cb.count += 1;
batch[id] = (batch[id] || 0) + 1;
//console.log("set listener for", next + ':' + id, batch[id], cb.count);
s3.event.on(next + ':' + id).event(function(){
batch[soul] = (batch[soul] || 0) + 1;
//console.log("set listener for", next + ':' + soul, batch[soul], cb.count);
s3.on(next + ':' + soul).event(function(){
cb.count -= 1;
//console.log("transaction", cb.count);
if(!cb.count){
s3.event.on(ack).emit();
s3.on(ack).emit();
this.off(); // MEMORY LEAKS EVERYWHERE!!!!!!!!!!!!!!!! FIX THIS!!!!!!!!!
}
});
@@ -125,39 +126,40 @@
var now = s3.next
, batch = s3.batch[s3.next];
s3.next = Gun.time.is();
Gun.obj.map(batch, function put(exists, id){
var node = gun.__.nodes[id]; // the batch does not actually have the nodes, but what happens when we do cold data? Could this be gone?
s3.put(s3.prefix + s3.prenode + id, node, function(err, reply){
console.log("s3 put reply", id, err, reply);
Gun.obj.map(batch, function put(exists, soul){
var node = gun.__.graph[soul]; // the batch does not actually have the nodes, but what happens when we do cold data? Could this be gone?
s3.put(s3.prefix + s3.prenode + soul, node, function(err, reply){
console.log("s3 put reply", soul, err, reply);
if(err || !reply){
put(exists, id); // naive implementation of retry TODO: BUG: need backoff and anti-infinite-loop!
put(exists, soul); // naive implementation of retry TODO: BUG: need backoff and anti-infinite-loop!
return;
}
s3.event.on(now + ':' + id).emit(200);
s3.on(now + ':' + soul).emit(200);
});
});
}
s3.next = s3.next || Gun.time.is();
s3.event = s3.event || Gun.on.split();
s3.on = s3.on || Gun.on.create();
s3.batching = s3.batching || 0;
s3.batched = s3.batched || {};
s3.batch = s3.batch || {};
s3.persisted = s3.persisted || {};
s3.wait = s3.wait || null;
s3.key = s3.key || function(key, node, cb){
var id = node._[Gun.sym.id];
if(!id){
return cb({err: "No ID!"});
s3.key = s3.key || function(key, soul, cb){
var meta = {};
meta[Gun._.soul] = soul = Gun.text.is(soul)? soul : (soul||{})[Gun._.soul];
if(!soul){
return cb({err: "No soul!"});
}
s3.put(s3.prefix + s3.prekey + key, '', function(err, reply){ // key is 2 bytes??? Should be smaller
console.log("s3 put reply", id, err, reply);
console.log("s3 put reply", soul, err, reply);
if(err || !reply){
s3.key(key, node, cb); // naive implementation of retry TODO: BUG: need backoff and anti-infinite-loop!
s3.key(key, soul, cb); // naive implementation of retry TODO: BUG: need backoff and anti-infinite-loop!
return;
}
cb();
}, {Metadata: {'#': id}});
}, {Metadata: meta});
}
gun.server.transport = (function(){
@@ -176,12 +178,13 @@
return tran.load(req, req.tran); // else load the state for the tab!
}
tran.load = function(req, cb){
var reply = {};
var reply = {}, key;
reply.headers = {'Content-Type': tran.json};
reply.headers['Gun-Sub'] = req.tab.sub = req.sub;
gun.load(req.url.key, function(node){
console.log("Loading for", req.tab);
tran.sub.scribe(req.tab, node._[Gun.sym.id]);
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
@@ -202,79 +205,84 @@
if(!req.body){ return cb({body: {err: "No body"}}) }
// raw test for now, no auth:
// should probably load all the nodes first? YES.
var context = Gun.chain.set.now.union.call(gun, req.body); // data safely transformed
//console.log("body?", req.body, context.err);
if(context.err){ return cb({body: {err: context.err}}) }
// WARNING! TODO: BUG! Do not send OK confirmation if amnesiaQuaratine is activated! Not until after it has actually been processed!!!
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);
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 ;
}
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, id, map){
now[id] = val;
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.
});
return now;
// 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!"}});
}
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, id){ // live push the stream out in realtime to every tab subscribed
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", id);
tran.push.on(id).emit(msg);
console.log("emit delta", soul);
tran.push(soul).emit(msg);
});
}
tran.post.s = {};
@@ -282,7 +290,6 @@
//console.log("<-- ", req.sub, req.tran ," -->");
req.tab = tran.sub.s[req.sub];
if(!req.tab){
console.log(req.url.query);
cb({
headers: {'Gun-Sub': ''}
,body: {err: "Please re-initialize sub."}
@@ -316,10 +323,10 @@
Gun.obj.del(tran.sub.s, tab.sub)
}, gun.__.opt.disconnect * mult * 1000); // in seconds
}
tran.sub.scribe = function(tab, id){
tran.sub.scribe = function(tab, soul){
tran.sub.s[tab.sub] = tab;
tab.subs = tab.subs || {};
tab.subs[id] = tab.subs[id] || tran.push.on(id).event(function(req){
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'];
@@ -349,13 +356,13 @@
});
}
reply.headers["Content-Type"] = tran.json;
if(res.chunk){
if(Gun.obj.has(res,'chunk')){
cb({
headers: reply.headers
,chunk: Gun.text.ify(res.chunk) + '\n'
})
}
if(res.body){
if(Gun.obj.has(res,'body')){
cb({
headers: reply.headers
,body: Gun.text.ify(res.body)
@@ -382,10 +389,10 @@
reply.headers[field] = val;
});
}
if(res.chunk && (!reply.body || Gun.list.is(reply.chunks))){
if(Gun.obj.has(res,'chunk') && (!reply.body || Gun.list.is(reply.chunks))){
(reply.chunks = reply.chunks || []).push(res.chunk);
}
if(res.body){
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);
@@ -393,7 +400,7 @@
}
}
tran.json = 'application/json';
tran.push = Gun.on.split();
tran.push = Gun.on.create();
return tran;
}());

View File

@@ -1,5 +1,153 @@
describe('Gun', function(){
var Gun = require('../gun');
var Gun = require('../gun')
, t = {};
describe('Utility', function(){
describe('Type Check', function(){
it('binary', function(){
expect(Gun.bi.is(false)).to.be(true);
expect(Gun.bi.is(true)).to.be(true);
expect(Gun.bi.is('')).to.be(false);
expect(Gun.bi.is('a')).to.be(false);
expect(Gun.bi.is(0)).to.be(false);
expect(Gun.bi.is(1)).to.be(false);
expect(Gun.bi.is([])).to.be(false);
expect(Gun.bi.is([1])).to.be(false);
expect(Gun.bi.is({})).to.be(false);
expect(Gun.bi.is({a:1})).to.be(false);
expect(Gun.bi.is(function(){})).to.be(false);
});
it('number',function(){
expect(Gun.num.is(0)).to.be(true);
expect(Gun.num.is(1)).to.be(true);
expect(Gun.num.is(Infinity)).to.be(true);
expect(Gun.num.is(NaN)).to.be(false);
expect(Gun.num.is('')).to.be(false);
expect(Gun.num.is('a')).to.be(false);
expect(Gun.num.is([])).to.be(false);
expect(Gun.num.is([1])).to.be(false);
expect(Gun.num.is({})).to.be(false);
expect(Gun.num.is({a:1})).to.be(false);
expect(Gun.num.is(false)).to.be(false);
expect(Gun.num.is(true)).to.be(false);
expect(Gun.num.is(function(){})).to.be(false);
});
it('text',function(){
expect(Gun.text.is('')).to.be(true);
expect(Gun.text.is('a')).to.be(true);
expect(Gun.text.is(false)).to.be(false);
expect(Gun.text.is(true)).to.be(false);
expect(Gun.text.is(0)).to.be(false);
expect(Gun.text.is(1)).to.be(false);
expect(Gun.text.is([])).to.be(false);
expect(Gun.text.is([1])).to.be(false);
expect(Gun.text.is({})).to.be(false);
expect(Gun.text.is({a:1})).to.be(false);
expect(Gun.text.is(function(){})).to.be(false);
});
it('list',function(){
expect(Gun.list.is([])).to.be(true);
expect(Gun.list.is([1])).to.be(true);
expect(Gun.list.is(0)).to.be(false);
expect(Gun.list.is(1)).to.be(false);
expect(Gun.list.is('')).to.be(false);
expect(Gun.list.is('a')).to.be(false);
expect(Gun.list.is({})).to.be(false);
expect(Gun.list.is({a:1})).to.be(false);
expect(Gun.list.is(false)).to.be(false);
expect(Gun.list.is(true)).to.be(false);
expect(Gun.list.is(function(){})).to.be(false);
});
it('obj',function(){
expect(Gun.obj.is({})).to.be(true);
expect(Gun.obj.is({a:1})).to.be(true);
expect(Gun.obj.is(0)).to.be(false);
expect(Gun.obj.is(1)).to.be(false);
expect(Gun.obj.is('')).to.be(false);
expect(Gun.obj.is('a')).to.be(false);
expect(Gun.obj.is([])).to.be(false);
expect(Gun.obj.is([1])).to.be(false);
expect(Gun.obj.is(false)).to.be(false);
expect(Gun.obj.is(true)).to.be(false);
expect(Gun.obj.is(function(){})).to.be(false);
});
it('fns',function(){
expect(Gun.fns.is(function(){})).to.be(true);
expect(Gun.fns.is('')).to.be(false);
expect(Gun.fns.is('a')).to.be(false);
expect(Gun.fns.is(0)).to.be(false);
expect(Gun.fns.is(1)).to.be(false);
expect(Gun.fns.is([])).to.be(false);
expect(Gun.fns.is([1])).to.be(false);
expect(Gun.fns.is({})).to.be(false);
expect(Gun.fns.is({a:1})).to.be(false);
expect(Gun.fns.is(false)).to.be(false);
expect(Gun.fns.is(true)).to.be(false);
});
it('time',function(){
t.ts = Gun.time.is();
expect(13 <= t.ts.toString().length).to.be.ok();
expect(Gun.num.is(t.ts)).to.be.ok();
expect(Gun.time.is(new Date())).to.be.ok();
});
});
describe('Text', function(){
it('ify',function(){
expect(Gun.text.ify(0)).to.be('0');
expect(Gun.text.ify(22)).to.be('22');
expect(Gun.text.ify([true,33,'yay'])).to.be('[true,33,"yay"]');
expect(Gun.text.ify({a:0,b:'1',c:[0,'1'],d:{e:'f'}})).to.be('{"a":0,"b":"1","c":[0,"1"],"d":{"e":"f"}}');
expect(Gun.text.ify(false)).to.be('false');
expect(Gun.text.ify(true)).to.be('true');
});
it('random',function(){
expect(Gun.text.random().length).to.be(24);
expect(Gun.text.random(11).length).to.be(11);
expect(Gun.text.random(4).length).to.be(4);
t.tr = Gun.text.random(2,'as'); expect((t.tr=='as'||t.tr=='aa'||t.tr=='sa'||t.tr=='ss')).to.be.ok();
});
});
describe('List', function(){
it('slit',function(){
(function(){
expect(Gun.list.slit.call(arguments, 0)).to.eql([1,2,3,'a','b','c']);
}(1,2,3,'a','b','c'));
});
it('sort',function(){
expect([{i:9},{i:4},{i:1},{i:-3},{i:0}].sort(Gun.list.sort('i'))).to.eql([{i:-3},{i:0},{i:1},{i:4},{i:9}]);
});
it('map',function(){
expect(Gun.list.map([1,2,3,4,5],function(v,i,t){ t(v+=this.d); this.d=v; },{d:0})).to.eql([1,3,6,10,15]);
expect(Gun.list.map([2,3,0,4],function(v,i,t){ if(!v){ return } t(v*=this.d); this.d=v; },{d:1})).to.eql([2,6,24]);
expect(Gun.list.map([true,false,NaN,Infinity,'',9],function(v,i,t){ if(i===3){ return 0 }})).to.be(0);
});
});
describe('Object', function(){
it('del',function(){
var obj = {a:1,b:2};
Gun.obj.del(obj,'a');
expect(obj).to.eql({b:2});
});
it('has',function(){
var obj = {a:1,b:2};
expect(Gun.obj.has(obj,'a')).to.be.ok();
});
it('copy',function(){
var obj = {"a":false,"b":1,"c":"d","e":[0,1],"f":{"g":"h"}};
var copy = Gun.obj.copy(obj);
expect(copy).to.eql(obj);
expect(copy).to.not.be(obj);
});
it('ify',function(){
expect(Gun.obj.ify('[0,1]')).to.eql([0,1]);
expect(Gun.obj.ify('{"a":false,"b":1,"c":"d","e":[0,1],"f":{"g":"h"}}')).to.eql({"a":false,"b":1,"c":"d","e":[0,1],"f":{"g":"h"}});
});
it('map',function(){
expect(Gun.obj.map({a:'z',b:'y',c:'x'},function(v,i,t){ t(v,i) })).to.eql({x:'c',y:'b',z:'a'});
expect(Gun.obj.map({a:'z',b:false,c:'x'},function(v,i,t){ if(!v){ return } t(i,v) })).to.eql({a:'z',c:'x'});
expect(Gun.obj.map({a:'z',b:3,c:'x'},function(v,i,t){ if(v===3){ return 0 }})).to.be(0);
});
});
});
it('ify', function(){
var data, test;