Skip to content

Instantly share code, notes, and snippets.

@jed
Created March 9, 2010 22:46
Show Gist options
  • Save jed/327241 to your computer and use it in GitHub Desktop.
Save jed/327241 to your computer and use it in GitHub Desktop.
/*
about (fab) v3
==============
the first version of (fab) was mostly a toy project, to see how function
chaining could help create a concise javascript DSL for building web
apps.
the second version of (fab) smoothed out some edges and focused on the
cases many micro-frameworks neglected: async and streaming.
the third version of (fab), currently in active development, will be a
total rewrite that reduces the core library to less than 1K of code,
implementing all functionality in custom apps that can be dropped in
anywhere in the (fab) hierarchy. (fab) itself will maintain no state,
have none of its own methods, objects, or prototypes, but will instead
act as glue to connect the three kinds of apps that anyone can build:
unary, binary, and ternary.
1. unary apps
-------------
a unary app is an app that talks to one other app. in the usual web
jargon, this is called a "handler", and in (fab) it looks like this:
*/
function hello( respond ) {
respond({ body: "hello world." })();
}
/*
in this case, the upstream app that calls this app passes a single
listener, which this app uses to write its response. the chained empty
call at the end indicates that the app is finished responding.
right now, this app has no access to information about the request, so
it can only return a static response. if the app wants more information,
it returns to the upstream app its own listener, like this
*/
function hello( respond ) {
return function( req ) {
respond({ body: "you are requesting " + req.url + "." })();
}
}
/*
every time one app calls another, the calling app can finish the
exchange by passing 0 arguments, and the called app can finish the
exchange by not returning a listener. each listener should only be
called once, and should be replaced with whatever function is returned
when the listener is called.
once we have an app, we can put it in a (fab) hierarchy:
*/
require( "fab" )
()
( hello )
()
/*
that's not very interesting, so let's talk about the other kinds of
apps.
2. binary apps
--------------
a binary app is an app that talks to two other apps: one upstream and
one downstream. in the usual web jargon, this is called "middleware",
and could look like this:
*/
function notify( downstream, upstream ) {
require( "sys" ).puts( "binary app called!" )
return downstream( upstream );
}
/*
in this example, notify is a simple binary app that logs something,
passes the upstream app to the downstream, and returns the downstream
listener to the upstream app. once it does this, this app will no longer
be called, and the upstream and downstream apps will be communicating
directly with each other. this is nice because once this app does its
job, it doesn't need add any overhead to the rest of the http
transaction unless it wants to.
binary apps will be a lot of the usual middleware: auth, headers, and
the like.
now there's only one more type of app to talk about.
3. ternary apps
---------------
a ternary app is an app that talks to three other apps: one upstream,
and two downstream. ternary apps can be used to route http requests at
call time, based on the characteristics of the request itself.
*/
function isGET( hit, miss, respond ) {
return function( req ) {
return ( req.method === "GET" ? hit : miss )( respond )( req );
}
}
/*
so in this case, isGET passes on to the "hit" app if the request method
is GET, and to the "miss" app otherwise. this is the kind of app that
will be used for URL routing and other call-time logic.
nullary and n-ary apps
----------------------
these apps don't exist in (fab), or in computer science, in a strict
sense. since a nullary app talks to no other apps, it cannot change
state, and therefore does not need to be called. an n-ary app that talks
to n other apps can be composed purely of ternary apps, so there's no
need for a special class for it.
summary
-------
putting all the apps we've made together, we have this:
*/
require( "fab" )
()
( isGET )
( notify )
( hello )
()
( function(){ respond( "GET requests only" )() } )
()
/*
note that anything in the (fab) hierarchy that is not a function will be
converted to the appropriate app: strings can become ternary path apps
or unary response apps, and numbers can become binary status handlers or
unary status responses, based on their location in the hierarchy.
of course, fab will ship with a few basic apps such as routing based on
strings and regular expressions, but they'll be totally overrideable,
and have no special access to any sort of central state (since none
exists).
the idea is to make (fab) all but invisible. the only way to interact
with (fab) is to write one of these three kinds of apps, and plug them
in the hierarchy wherever. so the bulk of (fab) development over the
next few will be (1) promoting a distributed ecosystem of reusable apps
a la the jQuery plugin system, and (2) deciding which apps are fit to be
included in the standard (fab) library (starting with the obvious
content lengths, auth, templating, et cetera).
let me know if you have any questions or feedback.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment