Saturday, September 18, 2010

Spike fab.accept

‹prev | My Chain | next›

Up today, I would like to explore code re-use of (fab) apps in the upcoming version 0.5 of fab.js. In my (fab) game, I have a dashboard resource that returns that list of players currently in the room:
      (route, /^\/player_status/)
( PRE )
( player_status )
() // PRE
() // route
Looking at this is the browser, I see:



Simple enough.

Now that I think about it though, I really prefer to see this from curl. It is nice to have it in HTML so I would just as soon not have to choose from one or the other. Aha! Code reuse opportunity!

I create a new /status resource that produces a simple text response:
  ( route, /^\/status/ )
(undefined, {headers: { "Content-Type": "text/plain"}})
( player_status )
()
I can access that via curl and see:
cstrom@whitefall:~/repos/my_fab_game$ curl http://localhost:4011/status
bob
fred

Cool! Quick, dirty resuable (fab) apps are quick and easy to roll. But...

There is the problem of using two different routes to represent the same thing. I would much prefer requesting a single resource and inspectint Accept headers so that something like his would work:
  ( route, /^\/status/ )
( accept, "text/html" )
( PRE )
( player_status )
()
()
(accept, "text/plain")
(undefined, {headers: { "Content-Type": "text/plain"}})
( player_status )
()
()
For this, I will need to write a new (fab) app—fab.accept.

I start as simple as possible:
fab.accept = function() {
function accept( write, mime_type ) {
return fab.stream( function( yes ) {
return fab.stream( function( no ) {
return write( function( write, head ) {
// console.log(inspect(head));
return ( head.headers.accept == mime_type ? yes : no )( write, head );
})();

});
});
}

return accept;
}();
That is close to boiler-plate (fab) code for ternary apps. Like all (fab) apps, it returns a function (accept()) that also returns a function. To get the conditional branch, that function also returns a function. If the condition matches (e.g. the Accept header matches), then the first application,
denoted by the yes local variable, is executed. Otherwise, the no application is executed (e.g. the next fab.accept or a 404).

I did have to use a console.log() (commented out), but only because I can never remember the attributes of the headers in fab.js / node.js. With that, I am ready to test fab.accept from the command line. There is no easy way to get curl to request plain text or HTML. Instead, I need to set the "Accept" HTTP header attribute with the -H option:
cstrom@whitefall:~/repos/my_fab_game$ curl -H "Accept: text/html" http://localhost:4011/status
<pre>bob
fred

</pre>
Nice, the HTML version of the resource is served up in response to "Accept: text/html". Can I get the plaint text version?
cstrom@whitefall:~/repos/my_fab_game$ curl -H "Accept: text/plain" http://localhost:4011/status
bob
foo

Nice!

I still have to polish off fab.accept (and it would be nice to have some tests), but that is a nice stopping point for the night. It is pretty cool that it so easy to write these little things!


Day #230

No comments:

Post a Comment