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). 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) * [Hyena](http://github.com/tibbe/hyena/tree/master)