hastache: Haskell implementation of Mustache templates

[ bsd3, deprecated, library, program, text ] [ Propose Tags ] [ Report a vulnerability ]
Deprecated in favor of mustache

Haskell implementation of Mustache templates (http://mustache.github.com/).

See homepage for examples of usage: http://github.com/lymar/hastache

In case version constraints need updating please comment here


[Skip to Readme]

Downloads

Note: This package has metadata revisions in the cabal description newer than included in the tarball. To unpack the package including the revisions, use 'cabal get'.

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

Versions [RSS] 0.1.1, 0.1.2, 0.1.3, 0.1.4, 0.1.5, 0.1.6, 0.2.0, 0.2.1, 0.2.2, 0.2.4, 0.3.2, 0.3.3, 0.4.1, 0.4.2, 0.5.0, 0.5.1, 0.6.0, 0.6.1
Change log ChangeLog
Dependencies base (>=4.4 && <4.11), blaze-builder (<0.5), bytestring (<0.11), containers (<0.6), directory (<1.4), filepath (<1.5), hastache, ieee754 (<0.9), mtl (>=2 && <2.3), process (<1.7), syb (<0.8), text (<1.2.5.0), transformers (<0.6) [details]
License BSD-3-Clause
Copyright Sergey S Lymar (c) 2011-2014
Author Sergey S Lymar <sergey.lymar@gmail.com>
Maintainer Daniil Frumin <dan at covariant.me>
Revised Revision 6 made by phadej at 2021-10-09T20:07:44Z
Category Text
Home page http://github.com/lymar/hastache
Bug tracker http://github.com/lymar/hastache/issues
Source repo head: git clone http://github.com/lymar/hastache
Uploaded by DaniilFrumin at 2014-12-23T10:43:03Z
Distributions FreeBSD:0.6.1
Reverse Dependencies 17 direct, 3635 indirect [details]
Executables mkReadme
Downloads 43554 total (45 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2014-12-23 [all 1 reports]

Readme for hastache-0.6.1

[back to package description]

Hastache

Haskell implementation of Mustache templates

Installation

cabal update
cabal install hastache

Usage

Read Mustache documentation for template syntax.

See Hastache hackage page.

Examples

Variables

import Text.Hastache 
import Text.Hastache.Context 
import qualified Data.Text.Lazy.IO as TL

main = hastacheStr defaultConfig (encodeStr template) (mkStrContext context)
    >>= TL.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.Text.Lazy.IO as TL 
import Data.Data 
import Data.Generics 

main = hastacheStr defaultConfig (encodeStr template) context
    >>= TL.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

Lists

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

List item by index

main = mapM_ (\(template,context) ->
    hastacheStr defaultConfig (encodeStr template) context >>= TL.putStrLn) 
        [(template1, mkStrContext context1),
         (template1, context2),
         (template3, context3)]

names = ["Nameless","Long Sky","Flying Snow","Broken Sword","Qin Shi Huang"]

template1 = concat [
    "{{heroes.1.name}}\n",
    "{{heroes.0.name}}\n"]

-- Context as function
context1 "heroes" = MuList $ map (mkStrContext . mkListContext) names
    where
    mkListContext name = \"name" -> MuVariable name
context1 _ = MuNothing

-- With Generics
data Hero = Hero { name :: String } deriving (Data, Typeable)
data Heroes = Heroes { heroes :: [Hero] } deriving (Data, Typeable)

context2 = mkGenericContext $ Heroes $ map Hero names

-- With Generics (another way)
template3 = concat [
    "{{heroName.3}}\n",
    "{{heroName.2}}\n"]

data HeroesStr = HeroesStr { heroName :: [String] } deriving (Data, Typeable)

context3 = mkGenericContext $ HeroesStr names
Long Sky
Nameless

Long Sky
Nameless

Broken Sword
Flying Snow

Conditional evaluation

Boolean

template = "{{#boolean}}true{{/boolean}}{{^boolean}}false{{/boolean}}"
context "boolean" = MuBool False
false

List

template = "{{^messages}}No new messages{{/messages}}"
context "messages" = MuList []
No new messages

Number

main = mapM_ (\ctx ->
    hastacheStr defaultConfig (encodeStr template) (mkStrContext ctx)
    >>= TL.putStrLn) [context1,context2]

template = "{{#msg}}{{msg}}{{/msg}}{{^msg}}No{{/msg}} new messages."

context1 "msg" = MuVariable (100 :: Int)
context2 "msg" = MuVariable (0 :: Int)
100 new messages.
No new messages.

Multiple constructors (in generic context)

#!/usr/local/bin/runhaskell
-- | Multiple constructors in generic contexts
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE OverloadedStrings  #-}
import           Data.Data
import           Data.Monoid
import           Data.Typeable                 ()

import qualified Data.Text.Lazy    as TL
import qualified Data.Text.Lazy.IO as TL
import           Text.Hastache
import           Text.Hastache.Context

data Hero = SuperHero { name      :: String
                      , powers    :: [String]
                      , companion :: String
                      }
          | EvilHero  { name   :: String
                      , minion :: String
                      }
          deriving (Show, Data, Typeable)

template :: String
template = mconcat [
    "{{#SuperHero}}\n",
    "Hero: {{name}}\n",
    " * Powers: {{#powers}}\n",
    "\n   - {{.}}{{/powers}} \n",
    " * Companion: {{companion}}\n",
    "{{/SuperHero}}\n",
    "{{#EvilHero}}\n",
    "Evil hero: {{name}}\n",
    " * Minion: {{minion}}\n",
    "{{/EvilHero}}"]

render :: Hero -> IO TL.Text
render = hastacheStr defaultConfig (encodeStr template)
       . mkGenericContext

main :: IO ()
main = do let batman = SuperHero "Batman" ["ht","ht"] "Robin"
          let doctorEvil = EvilHero "Doctor Evil" "Mini-Me"
          render batman >>= TL.putStrLn
          render doctorEvil >>= TL.putStrLn
Hero: Batman
 * Powers: 
   - ht
   - ht 
 * Companion: Robin

Evil hero: Doctor Evil
 * Minion: Mini-Me

Functions

template = "Hello, {{#reverse}}world{{/reverse}}!" 

context "reverse" = MuLambda (reverse . decodeStr)
Hello, dlrow!

Monadic functions

{-# LANGUAGE FlexibleContexts #-}
import Text.Hastache 
import Text.Hastache.Context
import qualified Data.Text.Lazy as TL 
import qualified Data.Text.Lazy.IO as TL 
import Control.Monad.State 

main = run >>= TL.putStrLn

run = evalStateT stateFunc ""

stateFunc :: StateT String IO TL.Text
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

Custom queries and field renaming

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeSynonymInstances #-}

-- Custom extension function for types that are not supported out of
-- the box in generic contexts
import Text.Hastache 
import Text.Hastache.Context 

import qualified Data.Text.Lazy as TL 
import qualified Data.Text.Lazy.IO as TL 

import Data.Data (Data, Typeable)
import Data.Decimal
import Data.Generics.Aliases (extQ)

data DecimalOrInf = Inf | Dec Decimal deriving (Data, Typeable)
deriving instance Data Decimal
data Test = Test {n::Int, m::DecimalOrInf} deriving (Data, Typeable)


val1 :: Test
val1 = Test 1 (Dec $ Decimal 3 1500)

val2 :: Test
val2 = Test 2 Inf

query :: Ext
query = defaultExt `extQ` f
  where f Inf = "+inf"
        f (Dec i) = show i

r "m" = "moo"
r x   = x

example :: Test -> IO TL.Text
example v = hastacheStr defaultConfig
                        (encodeStr template)
                        (mkGenericContext' r query v)

template = concat [ 
     "An int: {{n}}\n",
     "{{#moo.Dec}}A decimal number: {{moo.Dec}}{{/moo.Dec}}",
     "{{#moo.Inf}}An infinity: {{moo.Inf}}{{/moo.Inf}}"
     ] 

main = do
  example val1 >>= TL.putStrLn
  example val2 >>= TL.putStrLn
An int: 1
A decimal number: 1.500
An int: 2
An infinity: +inf

Generics big example

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)

More examples