Skip to content
This repository has been archived by the owner on Mar 3, 2023. It is now read-only.

Commit

Permalink
pulled each app out into its own file.
Browse files Browse the repository at this point in the history
  • Loading branch information
jed committed Sep 26, 2010
1 parent 84cbbc5 commit c21fe56
Show file tree
Hide file tree
Showing 21 changed files with 584 additions and 0 deletions.
45 changes: 45 additions & 0 deletions README.md
@@ -0,0 +1,45 @@
(fab) - a streaming javascript framework
========================================

(fab) is a lightweight toolkit that makes it easy to build asynchronous web apps. It takes advantage of the flexibility and functional nature of javascript to create a concise "DSL", without pre-compilation or magic scope hackery.

Here's an example of a "hello world" app:

module.exports = function( exports, imports ) {
return imports( function( run, node$listen, route, html ) {
with ( html ) return run
()
( node$listen, 0xFAB )
( HTML )
( BODY, { id: "show" } )
( "Hello, " )
( EM )
( route, /^\/(\w+)$/ )
( route.capture, 0 )
()
( "world!" )
()
( "!" )
()
()
();
})
}

## Getting started

To install (fab), use [npm](github.com/isaacs/npm):

npm install fab

Then, from the (fab) directory in your node folder, launch the `demo.js` example:

fab demo.js

Look in the source of the demo to see how the app was built.

Unfortunately, most of the current (fab) documentation reflects an older and deprecated API. This will be remedied shortly now that the (fab) API has started to gel. To stay updated on the progress of (fab), follow [@fabjs](http://twitter.com/fabjs) on Twitter.
7 changes: 7 additions & 0 deletions cli.js
@@ -0,0 +1,7 @@
#!/usr/bin/env node

require( "fab" )(
require(
require( "path" ).join( process.cwd(), process.argv[ 2 ] )
)
)
26 changes: 26 additions & 0 deletions concat.js
@@ -0,0 +1,26 @@
module.exports = function( exports ) {
return exports( function( write ) {
var buffer = ""
, types = { String: true, Buffer: true };

return function read( body, head ) {
// TODO: add timeout to flush?

if ( head && head.status >= 400 ) {
write( body, head )();
return function x(){ return x };
}

else if ( body && types[ body.constructor.name ] ) buffer += body;

else {
if ( buffer ) write = write( buffer );

buffer = "";
write = write.apply( undefined, arguments );
}

return arguments.length ? read : write;
}
});
}
86 changes: 86 additions & 0 deletions demo.js
@@ -0,0 +1,86 @@
// i'm not a huge fan of the commonJS require spec, but it'll do for now.
module.exports = function( exports, imports ) {

// imports is an async function that piggybacks on require.
// if you give it a list of string arguments, your callback will
// be called with the results in the same order. if you're feeling
// daring, like we are here, you can omit them and the imports
// function will toString your callback to find what you're looking
// for. a cool hack, but a hack nonetheless. "$" is replaced with
// "/" in module names.
return imports( function
( run
, node$listen
, route
, ignore
, write
, stream
, html
, head
, node$fs
, sleep
) {

// using with for our html module means we can have an awesome DSL for HTML
// templating. run is an app that recursively evaluates the entire stream once.
with ( html ) return run

// this is blank because we don't need to give run a downstream function,
// since we're not piping anything to stdout.
()

// this fires up a listener on port 4011
( node$listen, 0xFAB )

// let's route for static files. route is an app that takes two streams,
// one for matches and one for non-matches.
( route, /^\/static/ )
// we matched!

// the fs module takes one stream: the path name. it's just middleware
// that converts an upstream path name to the contents of the file.
( node$fs )
// first, get the current directory
( __dirname )

// then sleep for 500, just because we can
( sleep, 500 )

// then append the pathname
( head.url.pathname )
()
()
// we didn't match the /static url, so we keep going.

// now let's return the front page
( route, /^\/$/ )
// since we've with'ed the html app above, each uppercase element
// name <foo> here is a reference to html.<foo>. there's an app
// for each HTML5 element, and each of these apps is just middleware
// that outputs an open tag, pipes through its contents, and outputs
// a close tag when it's done.
( HTML )
( BODY, { id: "show" } )
( "Hello, " )

( EM )
// this pattern looks for a url that ends with an alphanumeric string
( route, /^\/(\w+)$/ )
// looks like a path was provided! here, route.capture outputs
// the captured path segment at the given index.
( route.capture, 0 )
()
// we didn't match, so let's output a generic hello.
( "world" )
()

( "!" )
()
()
()

// this is a catchall 404 for any paths that haven't matched.
( "Not found.", { status: 404 } )
();
})
}
25 changes: 25 additions & 0 deletions head.js
@@ -0,0 +1,25 @@
module.exports = function( exports ) {
var head = getter( "head" )
, prop, props = {
method: "",
url: "href protocol host auth hostname port pathname search query hash capture",
headers: "accept acceptCharset acceptEncoding acceptLanguage acceptRanges authorization cacheControl connection cookie contentLength contentType date expect from host ifMatch ifModifiedSince ifNoneMatch ifRange ifUnmodifiedSince maxForwards pragma proxyAuthorization range referer te upgrade userAgent via warning"
};

for ( prop in props ) {
var subprops = props[ prop ].split( " " );

head[ prop ] = getter( "head." + prop );
for ( var i = 0, subprop; subprop = subprops[ i++ ]; ) {
head[ prop ][ subprop ] = getter( "head." + prop + "." + subprop );
}
}

return exports( head );

function writer( val ) {
return new Function( "write", "head", "return write(" + val + ")" );
}

function getter( name ){ return writer( writer( name ) ) }
};
74 changes: 74 additions & 0 deletions html.js
@@ -0,0 +1,74 @@
module.exports = function( exports ) {
var tags = [

// open
"A ABBR ADDRESS ARTICLE ASIDE AUDIO B BB BDO BLOCKQUOTE BODY BUTTON\
CANVAS CAPTION CITE CODE COLGROUP DATAGRID DATALIST DD DEL DETAILS\
DFN DIALOG DIV DL DOCTYPE DT EM EVENTSOURCE FIELDSET FIGURE FOOTER\
FORM H1 H2 H3 H4 H5 H6 HEAD HEADER HTML I IFRAME INS KBD LABEL\
LEGEND LI MAP MARK MENU METER NAV NOSCRIPT OBJECT OL OPTGROUP\
OPTION OUTPUT P PRE PROGRESS Q RP RT RUBY SAMP SCRIPT SECTION\
SELECT SMALL SPAN STRONG STYLE SUB SUP TABLE TBODY TD TEXTAREA\
TFOOT TH THEAD TIME TITLE TR UL VAR VIDEO".split( " " ),

// closed
"AREA BASE BR COL COMMAND EMBED HR IMG INPUT KEYGEN LINK META PARAM\
SOURCE WBR".split( " " )

];

for ( var isVoid = 0, names; names = tags[ isVoid ]; isVoid++ ) {
for ( var i = 0, name; name = names[ i++ ]; ) {
elem[ name ] = elem( name.toLowerCase(), !!isVoid );
}
}

elem.DOCTYPE = function( write, dec ) {
return write( "<!DOCTYPE " )( dec )( ">\n" );
}

elem.COMMENT = function( write ) {
write = write( "<!-- " );

return function read( obj ) {
if ( !arguments.length ) return write( " -->" );
write = write( obj );
return read;
}
}

return exports( elem );

function elem( name, isVoid ) {
return function( write, obj ) {
write = write( "<" + name );
write = attrs( write )( obj )( ">" );

if ( isVoid ) return write;

return function read( arg ) {
if ( !arguments.length ) return write( "</" + name + ">" );

write = write.apply( undefined, arguments );
return read;
};
}
}

function attrs( write ) {
return function read( obj ) {
for ( var name in obj ) {
write = write( " " )( name )( "=" );
write = attr( write )( obj[ name ] );
}

return write;
}
}

function attr( write ) {
return function read( value ) {
return write( "\"" )( value )( "\"" );
}
}
};
3 changes: 3 additions & 0 deletions ignore.js
@@ -0,0 +1,3 @@
module.exports = function( exports ) {
return exports( function ignore(){ return ignore } );
}
29 changes: 29 additions & 0 deletions index.js
@@ -0,0 +1,29 @@
require.paths.unshift( __dirname );
module.exports = fab;

function fab( app ){ app( log, imports ) }

function log( data ) {
if ( data ) process.stdout.write( data );
return log;
}

function imports( exports ) {
var libs = []
, names = 1 in arguments
? libs.slice.call( arguments, 1 )
: exports.toString()
.split( /[^\w$]+/, exports.length + !!exports.name + 1 )
.slice( !!exports.name + 1 );

( function loop() {
var name = names.shift();

if ( !name ) exports.apply( undefined, libs );

else require( name.replace( /\W/g, "/" ) )( function( lib ) {
libs.push( lib );
loop();
}, imports );
})()
}
14 changes: 14 additions & 0 deletions log.js
@@ -0,0 +1,14 @@
module.exports = function( exports ) {
return exports( function( write, msg ) {
if ( msg ) {
console.log( msg );
return write;
}

return function read( body ) {
console.log( body );
write = write.apply( undefined, arguments );
return arguments.length ? read : write;
}
})
}
23 changes: 23 additions & 0 deletions method.js
@@ -0,0 +1,23 @@
module.exports = function( exports, imports ) {
return imports( function( stream ) {
var names = "GET PUT POST DELETE HEAD".split( " " );

function method( write, name ) {
return stream( function( yes ) {
return stream( function( no ) {
return write( function( write, head, body ) {
return ( head.method == name ? yes : no )( write, head, body );
})();
});
});
}

for ( var i = names.length; i--; ) ( function( name ) {
method[ name ] = function( write ) {
return method( write, name );
}
})( names[ i ] );

return exports( method );
})
}
24 changes: 24 additions & 0 deletions node/addContentLength.js
@@ -0,0 +1,24 @@
module.exports = function( exports, imports ) {
return imports( function( stream, render ) {
return exports( function( write ) {
return stream( function( upstream ) {
return write( function( write, head, body ) {
var buffered = stream()
, length = 0;

return upstream( render( function read( body, head ) {
buffered = buffered.apply( this, arguments );

if ( !arguments.length ) return buffered(
write( undefined, { headers: { "content-length": length } } )
);

if ( body ) length += body.length;

return read;
}, head, body ), head, body );
})();
});
});
}, "stream", "render" );
}

0 comments on commit c21fe56

Please sign in to comment.