hack-2009.4.52: readme.md
Hack: a sexy Haskell Webserver Interface
========================================
Hack is a brain-dead port of the brilliant Ruby [Rack](http://rack.rubyforge.org/) webserver interface.
Hack is made of 3 parts:
* hack: the spec
* hack-contrib: middleware
* hack-handler: drivers
The spec should be as stable as possible, hack-contrib is about middleware and tools, and hack-handler provides server connectivity.
Hack explained in one line
--------------------------
type Application = Env -> IO Response
What does a Hack app look like
------------------------------
module Main where
import Hack
import Hack.Handler.Happstack
hello :: Application
hello = \env -> return $ Response
{ status = 200
, headers = [ ("Content-Type", "text/plain") ]
, body = "Hello World"
}
main = run hello
1 minute tutorial
-----------------
### update cabal
cabal update
### install hack
cabal install happy
cabal install hack
cabal install hack-contrib
### pick a backend
cabal install hack-handler-happstack
### Create a Hack app
put the following code in `src/Main.hs`
module Main where
import Hack
import Hack.Handler.Happstack
hello :: Application
hello = \env -> return $ Response
{ status = 200
, headers = [ ("Content-Type", "text/plain") ]
, body = "Hello World"
}
main = run hello
### run
ghc --make -O2 Main.hs
./Main
It should be running on [http://127.0.0.1:3000](http://127.0.0.1:3000) now.
Middleware
-----------
### demo usage of middleware
module Main where
import Hack
import Hack.Handler.Happstack
import Hack.Contrib.Utils
import Hack.Contrib.Middleware.SimpleRouter
import Data.Default
hello :: Application
hello = \env -> return $ def {body = show env}
app :: Application
app = route [("/hello", hello), ("", hello)] empty_app
main = run app
### create a middleware
inside Hack.hs:
type Middleware = Application -> Application
since Haskell has curry, middleware api can be of type
Anything -> Application -> Application
just pass an applied middleware into a chain.
finally the source code of SimpleRouter.hs:
module Hack.Contrib.Middleware.SimpleRouter where
import Hack
import Hack.Contrib.Utils
import MPSUTF8
import Prelude hiding ((.), (^), (>))
import List (find, isPrefixOf)
type RoutePath = (String, Application)
route :: [RoutePath] -> Middleware
route h app = \env ->
let path = env.path_info
script = env.script_name
mod_env location = env
{ script_name = script ++ location
, path_info = path.drop (location.length)
}
in
case h.find (fst > (`isPrefixOf` path) ) of
Nothing -> app env
Just (location, app) -> app (mod_env location)
### Use the middleware stack
Rack provides a builder DSL, Hack just use a function. From `Contrib.Utils.hs`:
-- usage: app.use [content_type, cache]
use :: [Middleware] -> Middleware
use = reduce (<<<)
Handlers
--------
Just like Rack, once an application is written using Hack, it should work on any web server that provides a Hack handler. I'm only familiar with Kibro, so it became the first handler that's included.
The handler should expose only one function of type:
run :: Application -> IO ()
Spec
----
Hack spec = Rack spec :)
Please read [The Rack interface specification](http://rack.rubyforge.org/doc/files/SPEC.html).
Tip
---
### encoding can be tricky
1. hack uses utf8 bytes for IO
2. when dealing with html, sometimes, e.g. using template haskell, you have to
1. encode unicode string using: `escape_unicode_xml` for plain text
2. before rendering to response body, do `unescape_unicode_xml`
3. then use `u2b` to convert unicode string to utf8 bytes, and push to response
3. see, xml encoding and utf8 byte encoding are different, so we need
to convert twice
4. why bother `unescape_unicode_xml` at all? since we want the user
to be able to see unicode chars when viewing source!
5. these helpers are in `hack-contrib` and `mps`
6. the default error stream in hack takes unicode string as input. It will be
fixed in the next release.
Links
-----
### Resources
* [hack-contrib](http://github.com/nfjinjing/hack-contrib)
* [hack-handler-kibro](http://github.com/nfjinjing/hack-handler-kibro)
* [hack-handler-hyena](http://github.com/nfjinjing/hack-handler-hyena)
* [hack-handler-happstack](http://github.com/nfjinjing/hack-handler-happstack)
### Reference
* [Rack](http://rack.rubyforge.org/ )
* [Rack wiki](http://wiki.github.com/rack/rack)
* [rack-contrib](http://github.com/rack/rack-contrib/tree/master)
* [Chris Done's Blog @Kibro](http://chrisdone.com/blog/tags/Kibro.html)
* [Hyena](http://github.com/tibbe/hyena/tree/master)
* [Happstack](http://happstack.com/)
* [Jinjing's Blog @Hack](http://jinjing.easymic.com/tag/Hack)
* [Bamboo blog engine](https://github.com/nfjinjing/bamboo/tree)