scotty-hastache-0.2.1: src/Web/Scotty/Hastache.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ImpredicativeTypes #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeSynonymInstances #-}
{- | Hastache templating for Scotty
@
\{\-\# LANGUAGE OverloadedStrings \#\-\}
module Main where
import Text.Hastache
import Web.Scotty.Trans as S
import Web.Scotty.Hastache
main :: IO ()
main = scottyH' 3000 $ do
setTemplatesDir \"templates\"
-- ^ Setting up the director with templates
get \"/:word\" $ do
beam <- param \"word\"
setH \"action\" $ MuVariable (beam :: String)
-- ^ \"action\" will be binded to the contents of \'beam\'
hastache \"greet.html\"
@
Given the following template:
@
\<h1\>Scotty, {{action}} me up!\<\/h1\>
@
Upon the @GET \/beam@ the result will be:
@
\<h1\>Scotty, beam me up!\<\/h1\>
@
-}
module Web.Scotty.Hastache where
import Control.Arrow ((***))
import Control.Monad.State as State
import Data.IORef (newIORef, readIORef,
writeIORef)
import qualified Data.Map as M
import Data.Maybe (fromMaybe)
import Data.Monoid (mempty)
import Data.Text.Lazy (Text)
import Network.Wai (Application, Response)
import Network.Wai.Handler.Warp (Port)
import System.FilePath.Posix ((</>))
import Text.Hastache
import Text.Hastache.Context
import Web.Scotty.Trans as S
-- * Runners and types
-- | The runner to use instead of 'scotty'
scottyH :: (ScottyError e) => Port -> ScottyH e () -> IO ()
scottyH p s = do
(runH, runActionToIO) <- mkHStateRunners defaultConfig
scottyT p runH runActionToIO s
-- | The runner to use instead of 'scottyOpts'
scottyHOpts :: (ScottyError e) => Options -> ScottyH e () -> IO ()
scottyHOpts opts s = do
(runH, runActionToIO) <- mkHStateRunners defaultConfig
scottyOptsT opts runH runActionToIO s
-- | A type synonym for @ScottyT e HState@; with custom exception types
type ScottyH e = ScottyT e HState
-- | A type synonym for @ScottyT e HState@; with custom exception types
type ActionH e = ActionT e HState
-- ** Specialized types and runners
type ScottyH' = ScottyH Text
type ActionH' = ActionH Text
scottyH' :: Port -> ScottyH' () -> IO ()
scottyH' = scottyH
scottyHOpts' :: Options -> ScottyH' () -> IO ()
scottyHOpts' = scottyHOpts
-- * The DSL itself
-- ** Configuration
-- | Update the Hastache configuration as whole
setHastacheConfig :: MuConfig IO -> ScottyH e ()
setHastacheConfig conf = do
(_, tmap) <- lift State.get
lift . State.put $ (conf, tmap)
-- | Modify the Hastache configuration as whole
modifyHastacheConfig :: (MuConfig IO -> MuConfig IO) -> ScottyH e ()
modifyHastacheConfig f = lift $ State.modify (f *** id)
-- | Set the path to the directory with templates. This affects
-- how /both/ 'hastache' and the @{{> template}}@ bit searches for the
-- template files.
setTemplatesDir :: FilePath -> ScottyH e ()
setTemplatesDir dir = do
lift $ State.modify $ \(conf :: MuConfig IO, tmap) ->
(conf { muTemplateFileDir = Just dir }, tmap)
-- | Set the default extension for template files. This affects
-- how /both/ 'hastache' and the @{{> template}}@ bit searches for the
-- template files.
setTemplateFileExt :: String -> ScottyH e ()
setTemplateFileExt ext = do
lift $ State.modify $ \(conf :: MuConfig IO, tmap) ->
(conf { muTemplateFileExt = Just ext }, tmap)
-- ** Actions
-- | This is a function, just like 'S.html' or 'S.text'.
-- It takes a name of the template (the path is computed using the
-- information about the templates dir and template files extension)
-- and renders it using Hastache.
--
-- The variables that have been initialized using 'setH' are
-- substituted for their values, uninitialized variables are
-- considered to be empty/null.
hastache :: ScottyError e => FilePath -> ActionH e ()
hastache tpl = do
((conf :: MuConfig IO), tmap) <- lift State.get
let cntx a = fromMaybe MuNothing (M.lookup a tmap)
let tplFile = fromMaybe "." (muTemplateFileDir conf)
</> tpl
++ fromMaybe "" (muTemplateFileExt conf)
res <- liftIO $ hastacheFile conf tplFile (mkStrContext cntx)
html res
-- | Set the value of a mustache variable.
setH :: ScottyError e => String -> MuType IO -> ActionH e ()
setH x y = do
(conf, tmap) <- lift State.get
lift . State.put $ (conf, M.insert x y tmap)
-- * Internals
-- | State with the Hastache config
type HState = StateT (MuConfig IO, M.Map String (MuType IO)) IO
mkHStateRunners :: MuConfig IO -> IO (forall a. HState a -> IO a, HState Response -> IO Response)
mkHStateRunners conf = do
gstate <- newIORef undefined
let runH m = do
(r,(muconf,_)) <- runStateT m (conf, mempty)
writeIORef gstate muconf
return r
runActionToIO m = do
muconf <- readIORef gstate
evalStateT m (muconf, mempty)
return (runH, runActionToIO)
scottyHApp :: MuConfig IO -> ScottyH e () -> IO Application
scottyHApp conf defs = do
(runH, runActionToIO) <- mkHStateRunners conf
scottyAppT runH runActionToIO defs