packages feed

snap-web-routes-0.3.0.1: README.md

# snap-web-routes

## Type safe URLs for Snap

[`Snap web routes`](http://hackage.haskell.org/package/snap-web-routes) provides type safe URLs for Snap using [`web routes`](http://hackage.haskell.org/package/web-routes).

## How to use

The tutorial assumes you have a standard Snap app layout with an Application.hs and Site.hs. If your setup differs you'll need to adapt the instructions accordingly.

### `Application.hs`

To get going, you'll need to add a few things to `Application.hs`. This includes creating the URL data type and adding the routing function to our `App` data type.

    -- Enable a few extensions
    {-# LANGUAGE FlexibleInstances #-} -- Needed by
    {-# LANGUAGE TypeFamilies      #-} -- web-routes
    {-# LANGUAGE DeriveGeneric     #-} -- Needed to derive Generic
                                       -- for our URL data type

    -- Paths and params use Text.
    import Data.Text (Text)

    -- Snap.Web.Routes.Types exports everything you need to
    -- define your PathInfo and MonadRoute instances.
    import Snap.Web.Routes.Types

    -- Your URL data type.  Deriving a `Generic` instance gives
    -- you a `PathInfo` instance for free.
    data AppUrl
        = Count Int
        | Echo Text
        | Paths [Text]
          deriving (Generic)

    -- Extend your App type to include a routing function.
    data App = App
        { _routeFn :: AppUrl -> [(Text, Maybe Text)] -> Text
        }

    -- Thanks to the wonders of Generic, an empty instance
    -- definition is all we need. Alternately, you can implement
    -- toPathSegments and fromPathSegments yourself or use
    -- web-routes-th.
    instance PathInfo AppUrl

    -- Set URL (Handler App App) to your URL data type defined above
    -- and askRouteFn must point to the routing function you added to
    -- your App.
    instance MonadRoute (Handler App App) where
       type URL (Handler App App) = AppUrl
       askRouteFn = gets _routeFn

### `Site.hs`

Moving on to `Site.hs`, we'll setup handlers for each URL, as well initialise our app with a routing function.

    -- Snap.Web.Routes provides routing functions
    import Snap.Web.Routes

    -- Add your new routes using routeWith
    routes :: [(ByteString, Handler App App ())]
    routes = [ ("", routeWith routeAppUrl)
             , ("", serveDirectory "static")
             ]

    -- Define handlers for each value constructor in your URL data type.
    routeAppUrl :: AppUrl -> Handler App App ()
    routeAppUrl appUrl =
        case appUrl of
          (Count n)   -> writeText $ ("Count = " `T.append` (T.pack $ show n))
          (Echo text) -> echo text
          (Paths ps)  -> writeText $ T.intercalate " " ps

    -- You'll note that these are normal Snap handlers, except they can take
    -- values from the value constructor as arguments. This is a lot nicer than
    -- having to use getParam.
    echo :: T.Text -> Handler App App ()
    echo msg = heistLocal (bindString "message" msg) $ render "echo"

    -- Add the routing function to your app.
    app :: SnapletInit App App
    app = makeSnaplet "app" "An example snap-web-routes app." Nothing $ do
        addRoutes routes
        return $ App renderRoute

If you prefixed the routes in routeWith (e.g. `("/prefix", routeWith routeAppUrl)`) then use `renderRouteWithPrefix` instead:

    return . App $ renderRouteWithPrefix "/prefix"

If you are having trouble figuring out why a particular request isn't routing as expected, try replacing `routeWith` with `routeWithDebug`. It'll display the available routes, as well as any failed route parses. Just remember that it's **not suitable for production** use.

### Rendering URLs

Helper functions are provided for rendering URLs:

    echo :: T.Text -> Handler App App ()
    echo msg = do
        url <- showUrl $ Echo "this is a test"
        if msg == "test" then redirect (encodeUtf8 url) else renderEcho
      where
        renderEcho = heistLocal (I.bindSplices echoSplices) $ render "echo"
        echoSplices = do
            "message"  ## I.textSplice msg
            "countUrl" ## urlSplice (Count 10)

In the example above you'll find we use `urlSplice` to turn a URL into a splice, and `showUrl` to render a URL as Text.