Skip to content

Instantly share code, notes, and snippets.

@jed
Created May 18, 2010 04:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jed/404603 to your computer and use it in GitHub Desktop.
Save jed/404603 to your computer and use it in GitHub Desktop.

an update on fab v0.5

hey all, sorry for the lack of pushes this past week, but i'm still chugging on the v0.5 branch locally. here's an update of two changes i've made.

removed dependence on function length

this was a bit of a hard decision to make, but i've decided to stop using the function length property to differentiate unary apps from n-ary apps. it was a cute hack, but it needs to go for two reasons:

  • the google closure compiler doesn't respect argument count during compression. it removes any unused trailing arguments, which can end up changing the length of a function. this isn't too important now, but will be once (fab) starts moving client-ward.

  • requiring that unary apps have no arguments means that the request head has to be retrieved through a new function on every request. since this is such a common case that it makes more sense to pass it in the handler itself.

the tradeoff here is that unary apps are slightly more verbose:

// helloWorld.js, before
function() {
  this( "Hello, world!" );
}

// helloWorld.js, after
function() {
  this( function() {
    this( "Hello, world!" );
  })
}

but apps that access any data in the request head are now less verbose:

// capture.js, before
function() {
  var out = this;
  
  return function( head ) {
    out = out({ body: head.url.capture || [] });
    if ( out ) out();
  }
}

// capture.js, after
function() {
  this( function( body, head ) {
    this( head.url.capture || [] );
  })
}

i think this is a worthwhile compromise, for three reasons:

  • the defaults that (fab) uses for strings and other natives already make the simplest unary apps pretty easy.

  • (fab)'s goals lie in building reusable n-ary "middleware" over unary endpoints, so this case deserves optimization

  • it's always better to create functions at compile-time than at run-time.

built-in dependency management

in order to improve (fab)'s modularity, i'm moving from a jquery namespace pattern, in which all apps hang off the base fab app/object, to a RequireJS-inspired explicit requirement pattern. so built-in (fab) apps will now look like this:

function myApp( lib1, lib2 ) {       // n-ary arguments => define-time
  this( function( app1 ) {           // 1-ary arguments => compile-time
    this( function( body, head ) {   // 0-ary arguments => run-time

      /* app code here */

    });
  });
}

myApp.requires = [ "fab/lib1", "fab/lib2" ];
module.exports = myApp;

there are a few benefits to this approach:

  • the functions needed for each app are defined in three parts: a define-time function called during setup that pulls in all dependencies, a compile-time function called during app construction that pulls in the required apps, and finally a call-time function that is called for each request.

  • apps are more decentralized, since there is no global symbol on which all dependencies rely.

  • nested dependencies can be determined at define-time, since each app file describes what it needs.

  • eventually, a client-side (fab) app will buildable on the server with only the apps actually used, to keep download sizes as small as possible.

  • apps will be more compressable, since all ingedients in an app are local symbols.

this means that the base fab app is now just an async require module, so that the boilerplate for a simple app will look something like this:

require( "fab" ).call( function( site, listen ) {
  
  ( site )
    ( listen, 0xFAB )
    ( "Hello, world!" )

}, "fab/site", "fab/listen" );

it's a bit of a pain to have to specify the exact apps you're using in the exact order you need them, so i'll probably also add support for a wildcard to generate a namespace object too:

require( "fab" ).call( function( fab ) {
  
  ( fab.site )
    ( fab.listen, 0xFAB )
    ( "Hello, world!" )

}, "fab/*" );

well, that's it for now. feedback welcome in the comments below.

Copy link

ghost commented May 18, 2010

Good changes all around! fab is evolving nicely.

Yes, the wildcard thing would be useful...

And, jed, when can we expect this 0.5 release? Let us know on Twitter!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment