Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
A sinatra-ish framework written in haskell, riding on top of Hack
Haskell
branch: master

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
bin
src
.gitignore
HISTORY.markdown.lhs
LICENSE public domain license.
README.markdown
Setup.hs
bird.cabal bird 19: fly your bird over any port. See the HISTORY

README.markdown

Bird

A sinatra-ish web framework written in haskell, riding on top of Hack.

Why?

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?

Install

λ cabal update && cabal install bird

Note: make sure $HOME/.cabal/bin is in your PATH.

Checkout the bird command

λ 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)

Create an app

λ bird hatch StarWars
  A fresh bird app has been created in StarWars.

Compile your app

λ cd StarWars
λ bird nest 
  [1 of 2] Compiling StarWars           ( StarWars.hs, StarWars.o )
  [2 of 2] Compiling Main               ( Main.hs, Main.o )
  Linking Main ...
λ

Start your app (defaults to port 3000)

λ bird fly
  A bird was just spotted in flight at http://localhost:3000

Try it out

λ curl http://localhost:3000
  Hello, Bird!

Improvise!

-- 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.

API

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?"

Notes

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
Something went wrong with that request. Please try again.