Haskell implementation of Mustache templates
cabal update
cabal install hastache
Read Mustache documentation for template syntax.
import Text.Hastache
import Text.Hastache.Context
import qualified Data.ByteString.Lazy as LZ
main = hastacheStr defaultConfig (encodeStr template) (mkStrContext context)
>>= LZ.putStrLn
template = "Hello, {{name}}!\n\nYou have {{unread}} unread messages."
context "name" = MuVariable "Haskell"
context "unread" = MuVariable (100 :: Int)
Hello, Haskell!
You have 100 unread messages.
With Generics
{-# LANGUAGE DeriveDataTypeable #-}
import Text.Hastache
import Text.Hastache.Context
import qualified Data.ByteString.Lazy as LZ
import Data.Data
import Data.Generics
main = hastacheStr defaultConfig (encodeStr template) context
>>= LZ.putStrLn
data Info = Info {
name :: String,
unread :: Int
} deriving (Data, Typeable)
template = "Hello, {{name}}!\n\nYou have {{unread}} unread messages."
context = mkGenericContext $ Info "Haskell" 100
template = concat [
"{{#heroes}}\n",
"* {{name}} \n",
"{{/heroes}}\n"]
context "heroes" = MuList $ map (mkStrContext . mkListContext)
["Nameless","Long Sky","Flying Snow","Broken Sword","Qin Shi Huang"]
where
mkListContext name = \"name" -> MuVariable name
* Nameless
* Long Sky
* Flying Snow
* Broken Sword
* Qin Shi Huang
With Generics
data Hero = Hero { name :: String } deriving (Data, Typeable)
data Heroes = Heroes { heroes :: [Hero] } deriving (Data, Typeable)
template = concat [
"{{#heroes}}\n",
"* {{name}} \n",
"{{/heroes}}\n"]
context = mkGenericContext $ Heroes $ map Hero ["Nameless","Long Sky",
"Flying Snow","Broken Sword","Qin Shi Huang"]
Another Generics version
data Heroes = Heroes { heroes :: [String] } deriving (Data, Typeable)
template = concat [
"{{#heroes}}\n",
"* {{.}} \n",
"{{/heroes}}\n"]
context = mkGenericContext $ Heroes ["Nameless","Long Sky","Flying Snow",
"Broken Sword","Qin Shi Huang"]
template = "Hello, {{#reverse}}world{{/reverse}}!"
context "reverse" = MuLambda (reverse . decodeStr)
Hello, dlrow!
{-# LANGUAGE FlexibleContexts #-}
import Text.Hastache
import Text.Hastache.Context
import qualified Data.ByteString.Lazy as LZ
import Control.Monad.State
main = run >>= LZ.putStrLn
run = evalStateT stateFunc ""
stateFunc :: StateT String IO LZ.ByteString
stateFunc =
hastacheStr defaultConfig (encodeStr template) (mkStrContext context)
template = "{{#arg}}aaa{{/arg}} {{#arg}}bbb{{/arg}} {{#arg}}ccc{{/arg}}"
context "arg" = MuLambdaM $ arg . decodeStr
arg :: MonadState String m => String -> m String
arg a = do
v <- get
let nv = v ++ a
put nv
return nv
aaa aaabbb aaabbbccc
data Book = Book {
title :: String,
publicationYear :: Integer
} deriving (Data, Typeable)
data Life = Life {
born :: Integer,
died :: Integer
} deriving (Data, Typeable)
data Writer = Writer {
name :: String,
life :: Life,
books :: [Book]
} deriving (Data, Typeable)
template = concat [
"Name: {{name}} ({{life.born}} - {{life.died}})\n",
"{{#life}}\n",
"Born: {{born}}\n",
"Died: {{died}}\n",
"{{/life}}\n",
"Bibliography:\n",
"{{#books}}\n",
" {{title}} ({{publicationYear}})\n",
"{{/books}}\n"
]
context = mkGenericContext Writer {
name = "Mikhail Bulgakov",
life = Life 1891 1940,
books = [
Book "Heart of a Dog" 1987,
Book "Notes of a country doctor" 1926,
Book "The Master and Margarita" 1967]
}
Name: Mikhail Bulgakov (1891 - 1940)
Born: 1891
Died: 1940
Bibliography:
Heart of a Dog (1987)
Notes of a country doctor (1926)
The Master and Margarita (1967)
- Hastache test
- Real world example: README.md file generator