A sinatra-ish web framework written in haskell, riding on top of Hack.
Sinatra has a beautiful, simple, elegant syntax, but it's essentially an attempt to bring pattern matching to a language never intended for pattern matching. Why not attempt something similar in a language with not just beautiful pattern matching, but with all the declarative bells and whistles: lazy evaluation, first-class functions, currying, polymorphism?
λ cabal update && cabal install bird
Note: make sure $HOME/.cabal/bin is in your PATH.
λ bird
Usage: bird action [options]
Actions:
hatch [name] -> create a new Bird app, takes the name as an argument, for example `bird hatch StarWars`
nest -> compile your Bird app
fly [port] -> expose your Bird app to the world (port 3000 by default)
λ bird hatch StarWars
A fresh bird app has been created in StarWars.
λ cd StarWars
λ bird nest
[1 of 2] Compiling StarWars ( StarWars.hs, StarWars.o )
[2 of 2] Compiling Main ( Main.hs, Main.o )
Linking Main ...
λ
λ bird fly
A bird was just spotted in flight at http://localhost:3000
λ curl http://localhost:3000
Hello, Bird!
-- StarWars.bird.hs
import Data.String.Utils (join)
get ["droids"] = do
body "These aren't the droids you're looking for. Move along."
status 404
post ["jedi"] = do
name <- param "name"
teacher <- param "teacher"
case teacher of
Just "Yoda" -> body "The force is strong with this one!" >> status 201
_ -> body "Weak the force is with this one." >> status 400
get ("force":xs) = do
body $ "May the force be with you " ++ (join ", " xs) ++ "!"
get [] = do
name <- param "name"
log "I'm about to greet a Jedi. Teehee!"
body $ "Greetings, " ++ (maybe "Jedi!" id name)
Now recompile your app and start it flying:
λ bird nest
λ bird fly &
λ curl -i http://localhost:3000/force/Han/Chewie
HTTP/1.1 200 OK
Connection: close
Content-Type: text/html
Date: Sat, 31 Jul 2010 14:07:17 GMT
Server: Happstack/0.5.0.2
May the force be with you Han, Chewie!
λ curl -i -X POST http://localhost:3000/jedi -d name=Luke -d teacher=Yoda
HTTP/1.1 201 Created
Connection: close
Content-Type: text/html
Date: Sat, 21 Aug 2010 21:38:11 GMT
Server: Happstack/0.5.0.2
The force is strong with this one!
λ curl -i http://localhost:3000/droids
HTTP/1.1 404 Not Found
Connection: close
Content-Type: text/html
Date: Sat, 31 Jul 2010 14:08:35 GMT
Server: Happstack/0.5.0.2
These aren't the droids you're looking for. Move along.
You have four functions to implement: get, post, put, and delete. They each accept a Bird Request.
Inside the function body, you can use the following methods (don't worry, this is a growing list):
param :: String -> Maybe String
-- ex: for the request GET /droids?name=c3po,
-- then `p <- param "name"' would bind the value `Just "c3po"' to the variable "p"
body :: String -> BirdResponder ()
-- takes a string and sets the Http Response body to whatever the string contained.
status :: Integer -> BirdResponder ()
-- takes a number, and sets the HTTP Reponse header "Status" to that number.
mime :: String -> BirdResponder ()
-- sets the mime type to whatever you provide
-- ex: get [] = body "Hello World" >> mime "text/plain"
header :: String -> String -> BirdResponder ()
-- creates/updates a header
-- ex: get [] = body "Hello World" >> header "X-Powered-By" "BIRD!"
log :: String -> BirdResponder ()
-- adds to the log
-- ex: get [] = body "Hello World" >> log "Why did I just greet the world?"
This project is still in its infancy. Coming features:
- support for multipart forms submissions
- helpers for popular html generation solutions (Hamlet, HStringTemplate, HAXML, BlazeHTML, etc.)
- WAI support
- static asset serving
- support for sending files