snap-web-routes-0.1.0.0: src/Snap/Web/Routes.hs
{-|
This module provides a ready to use implementation of `web-routes` for Snap.
To get going, you'll need to add a few things to `Application.hs`.
> {-# LANGUAGE DeriveGeneric #-}
> {-# LANGUAGE FlexibleInstances #-}
> {-# LANGUAGE TypeFamilies #-}
DeriveGeneric is used to derive the `PathInfo` instance for your URL data type,
the rest are needed by `web-routes`.
> import Data.Text (Text)
> import Snap.Web.Routes
"Snap.Web.Routes" exports the data types needed to define your `PathInfo` and
`MonadRoute` instances below.
> data AppUrl
> = Count Int
> | Echo Text
> | Paths [Text]
> deriving (Generic)
Define your application's URL data type. Deriving a `Generic` instance gives
you a `PathInfo` instance for free.
> data App = App
> { _routeFn :: AppUrl -> [(Text, Maybe Text)] -> Text
> }
Extend your App type to include a routing function.
> instance PathInfo AppUrl
Get your free PathInfo instance. Alternatives are to use `web-routes-th` or
implement PathInfo yourself.
> instance MonadRoute (Handler App App) where
> type URL (Handler App App) = AppUrl
> askRouteFn = gets _routeFn
Define your MonadRoute instance. In particular, `type URL (Handler App App)`
must be set to your URL data type defined above and `askRouteFn` should point
to the routing function you added to your App type.
Moving on to `Site.hs`.
> import Snap.Web.Routes
Snap.Web.Routes provides a convenience router function you'll need.
> routes :: [(ByteString, Handler App App ())]
> routes = [ ("", serveDirectory "static")
> , ("", routeWith routeAppUrl)
> ]
Add your routes to the bottom of the routes list using routeWith.
> 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
> echo :: T.Text -> Handler App App ()
> echo msg = heistLocal (bindString "message" msg) $ render "echo"
Define the handler for each data constructor in your URL data type.
> app :: SnapletInit App App
> app = makeSnaplet "app" "An example application with snap-web-routes." Nothing $ do
> addRoutes routes
> return $ App renderRoute
Lastly, add the routing function to your app. If you prefixed the routes in routeWith:
> , ("/prefix", routeWith routeAppUrl)
then use `renderRouteWithPrefix` instead:
> return . App $ renderRouteWithPrefix "/prefix"
|-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
module Snap.Web.Routes
( renderRoute
, renderRouteWithPrefix
, routeWith
, heistUrl
, gets
, Generic
, MonadRoute(..)
, PathInfo(..)
) where
import Control.Monad.State (lift, gets)
import Data.Text (Text, append, pack)
import Heist (HeistT)
import Snap.Core
import Snap.Web.Routes.Heist
import Snap.Snaplet
import Web.Routes
instance (MonadRoute m) => MonadRoute (HeistT n m) where
type URL (HeistT n m) = URL m
askRouteFn = lift askRouteFn
routeWith :: (PathInfo url, MonadSnap m) => (url -> m ()) -> m ()
routeWith router =
do rq <- getRequest
case fromPathInfo $ rqPathInfo rq of
(Left e) -> writeText (pack e)
(Right url) -> router url
renderRoute :: PathInfo url => url -> [(Text, Maybe Text)] -> Text
renderRoute = renderRouteWithPrefix ""
renderRouteWithPrefix :: PathInfo url => Text -> url -> [(Text, Maybe Text)] -> Text
renderRouteWithPrefix prefix url params = prefix `append` toPathInfoParams url params