I crashed and burned last night trying to write my own v0.5 fab.js app for my (fab) game. Tonight I hope to do a bit better.
I am trying to provide a
/player_status
resource that will return a plain text representation of all the players currently in a room:(route, /^\/player_status/)The players are all stored in CouchDB. To get all of them, I have an
( player_status )
()
all
callback method on the players
object that can be used something like:players.all(function(list) {Although I kinda/sorta got things working last night, I am going to toss that work. I ended up with very weird stacktraces that leads me to believe that I am on the wrong track.
list.forEach(function(player) {
console.log(player);
});
});
The normal skeleton of a (fab) app looks something like this:
fab.myApp = function( write ) {I cannot use that in this case because there are no upstream (further from the browser) apps to be
return function read( body ) {
var done;
// app logic here
return done ? write : read;
}
}
read
. After much fiddling, I end up with this:
function player_status(write) {Amazingly, this works:
return write(function(write) {
return fab.stream(function(stream) {
stream(write(undefined, { headers: { "Content-Type": "text/plain"} }));
players.all(function(list) {
list.forEach(function(player) {
stream(write(player._id + "\n"));
});
stream(write("\n"));
stream(write());
});
});
});
cstrom@whitefall:~$ curl http://localhost:4011/player_status -iI must confess that I am a little puzzled why it works. My best guess, the first line of the app body:
HTTP/1.1 200 OK
Content-Type: text/plain
Connection: keep-alive
Transfer-Encoding: chunked
bob
return write(function(write) {Behaves identically to (fab) middleware with an upstream app:
(route, /^\/player_status/)My
( middleware )
( upstream )
()
player_status
is writing back to the browser, reading from the supplied anonymous function.The next line, I find bizarre:
return fab.stream(function(stream) {The
fab.stream
app queues up upstream messages to be sent back to the browser. The fab.stream
app knows to send the entire queue when the upstream app signals the last of its output with an empty argument list. In this case, the anonymous function supplied as the argument to fab.stream
serves as the upstream app (the signal to terminate is the last line:function(list) {The reason that I find this bizarre is that
list.forEach(function(player) {
stream(write(player._id + "\n"));
});
stream(write("\n"));
stream(write());
});
fab.stream
takes a single argument—the downstream / write stream:fab.stream = function stream( write, queue ) {In my working-through-a-small-miracle code, however, I am not supplying a write stream to the browser. I am supplying the opposite—an upstream app from which data will be read.
queue = queue || [];
//...
return function read() {
if ( !arguments.length ) return write ? write( drain ) : drain;
queue[ length++ ] = arguments;
return read;
}
}
Somehow the
read()
function inside fab.stream
, the function that is responsible for writing back downstream to the browser, is being supplied as the argument to the anonymous function that is supplied to fab.stream
. I believe that this is due to if-function-then-apply-with-arguments logic in the main fab app itself.
fab.stream
returns the read()
function, which is then applied with the arguments to fab.stream
—my fake downstream app in this case.Ugh. Not sure about that explanation. I kinda/sorta think I understand, but that is not true understanding.
I think I'll sleep on in at this point and pick back up with more investigation tomorrow.
Day #219
it may help to realize there's no such thing as a single upstream app anymore... the entire stream of calls after the current one constitute everything upstream.
ReplyDeletethink of fab.stream as a pause button that takes everything upstream and returns a function that replays it. the main use of this when you have async/non-blocking code that you need to handle before continuing.
if you can distill this into a gist i'd like to help you troubleshoot, just lemme know, okay?