packages feed

hastache 0.6.0 → 0.6.1

raw patch · 7 files changed

+477/−83 lines, 7 filesdep +processdep −utf8-stringdep ~basedep ~blaze-builderdep ~bytestringnew-component:exe:mkReadme

Dependencies added: process

Dependencies removed: utf8-string

Dependency ranges changed: base, blaze-builder, bytestring, containers, directory, filepath, ieee754, mtl, syb, text, transformers

Files

ChangeLog view
@@ -1,3 +1,24 @@+# 0.6.1++- Fixing documentation typos+- Implementing basic context merging (see+  <https://github.com/lymar/hastache/issues/31>)+- `mkGenericContext'` takes an additional `(String -> String)`+  argument which is applied to record field names, much like aeson's+  `fieldLabelModifier`+- Removing unnecessary `utf8-string` dependency+- Implementing the generic context creation for `Maybe` (see+  <https://github.com/lymar/hastache/issues/38> and+  `nestedPolyGenericContextTest` test), `Either`, `()`, and `Version`.+- `mkReadme` is now officially an executable+- Big change: custom extensions for generic contexts (should solve+  #30). See the documentation for `Text.Hastache.Context` and the+  `genericCustom.hs` example.+- Making Hastache work with new base-4.8 (see #41)++Thanks to: Tobias Florek, Edsko de Vries, Janne Hellsten, @clinty,+Stefan Kersten, Herbert Valerio Riedel, and others+ # 0.6.0  - Switching from lazy ByteString to lazy Text
README.md view
@@ -197,6 +197,65 @@ No new messages. ``` +Multiple constructors (in generic context)++```haskell+#!/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  ```haskell@@ -243,6 +302,70 @@ aaa aaabbb aaabbbccc ``` +#### Custom queries and field renaming++```haskell+{-# 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  ```haskell@@ -298,3 +421,4 @@   * [Hastache test](https://github.com/lymar/hastache/blob/master/tests/test.hs)  * Real world example: [README.md file generator](https://github.com/lymar/hastache/blob/master/mkReadme.hs)+ * [examples/ directory](https://github.com/lymar/hastache/tree/master/examples)
Text/Hastache.hs view
@@ -16,8 +16,8 @@ Simplest example:  @-import Text.Hastache -import Text.Hastache.Context +import           Text.Hastache+import           Text.Hastache.Context import qualified Data.Text.Lazy.IO as TL  main = do @@ -41,22 +41,23 @@ Using Generics:  @-import Text.Hastache -import Text.Hastache.Context +{&#45;\# LANGUAGE DeriveDataTypeable, OverloadedStrings \#&#45;}++import           Text.Hastache+import           Text.Hastache.Context import qualified Data.Text.Lazy.IO as TL-import Data.Data -import Data.Generics - -data Info = Info { -    name    :: String, -    unread  :: Int +import           Data.Data++data Info = Info {+    name    :: String,+    unread  :: Int     } deriving (Data, Typeable) -main = do -    res <- hastacheStr defaultConfig (encodeStr template) -        (mkGenericContext inf) -    TL.putStrLn res -  where +main = do+    res <- hastacheStr defaultConfig template+        (mkGenericContext inf)+    TL.putStrLn res+  where     template = \"Hello, {{name}}!\\n\\nYou have {{unread}} unread messages.\"     inf = Info \"Haskell\" 100 @@@ -71,6 +72,7 @@     , MuType(..)     , MuConfig(..)     , MuVar(..)+    , composeCtx     , htmlEscape     , emptyEscape     , defaultConfig@@ -86,12 +88,13 @@ import Control.Monad.Trans.Maybe (MaybeT(MaybeT), runMaybeT) import Data.AEq (AEq,(~==)) import Data.Functor ((<$>))-import Data.Int import Data.IORef+import Data.Int import Data.Maybe (isJust)-import Data.Monoid (mappend, mempty)+import Data.Monoid (Monoid, mappend, mempty) import Data.Text hiding (map, foldl1) import Data.Text.IO+import Data.Version (Version) import Data.Word import Prelude hiding (putStrLn, readFile, length, drop, tail, dropWhile, elem,     head, last, reverse, take, span, null)@@ -119,8 +122,24 @@     Text            -- ^ Variable name     -> m (MuType m) -- ^ Value +instance (Monad m) => Monoid (MuContext m) where+    mempty = const $ return MuNothing+    a `mappend` b = \v -> do+        x <- a v+        case x of+            MuNothing -> b v+            _ -> return x++-- | Left-leaning compoistion of contexts. Given contexts @c1@ and+-- @c2@, the behaviour of @(c1 <> c2) x@ is following: if @c1 x@+-- produces 'MuNothing', then the result is @c2 x@. Otherwise the+-- result is @c1 x@. Even if @c1 x@ is 'MuNothing', the monadic+-- effects of @c1@ are still to take place.+composeCtx :: (Monad m) => MuContext m -> MuContext m -> MuContext m+composeCtx = mappend+ class Show a => MuVar a where-    -- | Convert to Lazy ByteString+    -- | Convert to lazy 'Data.Text.Lazy.Text'     toLText   :: a -> TL.Text     -- | Is empty variable (empty string, zero number etc.)     isEmpty   :: a -> Bool @@ -163,7 +182,8 @@ instance MuVar Word64  where {toLText = withShowToText; isEmpty = numEmpty} instance MuVar ()      where {toLText = withShowToText} -    +instance MuVar Version where {toLText = withShowToText }+ instance MuVar Char where     toLText = TL.singleton @@ -212,7 +232,7 @@     muEscapeFunc        :: TL.Text -> TL.Text,          -- ^ Escape function ('htmlEscape', 'emptyEscape' etc.)     muTemplateFileDir   :: Maybe FilePath,-        -- ^ Directory for search partial templates ({{> templateName}})+        -- ^ Directory for search partial templates (@{{> templateName}}@)     muTemplateFileExt   :: Maybe String,         -- ^ Partial template files extension     muTemplateRead      :: FilePath -> m (Maybe Text)@@ -220,22 +240,23 @@         --   template could not be found.     } --- | Convert String to Text+-- | Convert 'String' to 'Text' encodeStr :: String -> Text encodeStr = T.pack --- | Convert String to Lazy Text+-- | Convert 'String' to Lazy 'Data.Text.Lazy.Text' encodeStrLT :: String -> TL.Text encodeStrLT = TL.pack --- | Convert Text to String+-- | Convert 'Text' to 'String' decodeStr :: Text -> String decodeStr = T.unpack --- | Convert Lazy Text to String+-- | Convert Lazy 'Data.Text.Lazy.Text' to 'String' decodeStrLT :: TL.Text -> String decodeStrLT = TL.unpack +-- | isMuNothing x = x == MuNothing isMuNothing :: MuType t -> Bool isMuNothing MuNothing = True isMuNothing _ = False@@ -352,7 +373,7 @@     (before, after) = breakOn close str  trimCharsTest :: Char -> Bool-trimCharsTest = (`Prelude.elem` " \t")+trimCharsTest = (`Prelude.elem` [' ', '\t'])  trimAll :: Text -> Text trimAll = dropAround trimCharsTest
Text/Hastache/Context.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE CPP #-}+{-# LANGUAGE RankNTypes #-} -- Module:      Text.Hastache.Context -- Copyright:   Sergey S Lymar (c) 2011-2013  -- License:     BSD3@@ -14,11 +15,16 @@       mkStrContext     , mkStrContextM     , mkGenericContext+    , mkGenericContext'+    , Ext+    , defaultExt     ) where   import Data.Data import Data.Generics import Data.Int+import Data.Version (Version)+import Data.Ratio (Ratio) import Data.Word  import qualified Data.ByteString as BS@@ -41,11 +47,19 @@ mkStrContextM :: Monad m => (String -> m (MuType m)) -> MuContext m mkStrContextM f a = decodeStr a ~> f +type Ext = forall b. (Data b, Typeable b) => b -> String++-- | @defaultExt ==@ 'gshow'+defaultExt :: Ext+defaultExt = gshow+ {- |  Make Hastache context from Data.Data deriving type  Supported field types: + * ()+  * String    * Char@@ -86,6 +100,12 @@    * Bool + * Version++ * Maybe @a@ (where @a@ is a supported datatype)++ * Either @a@ @b@ (where @a@ and @b@ are supported datatypes)+  * Data.Text.Text -> Data.Text.Text   * Data.Text.Text -> Data.Text.Lazy.Text@@ -142,7 +162,7 @@ example = hastacheStr defaultConfig (encodeStr template)      (mkGenericContext context)     where-    template = concat $ map (++ \"\\n\") [+    template = unlines [         \"string: {{stringField}}\",         \"int: {{intField}}\",         \"data: {{dataField.someField}}, {{dataField.anotherField}}\",@@ -199,14 +219,79 @@ @  -}-                     #if MIN_VERSION_base(4,7,0) mkGenericContext :: (Monad m, Data a, Typeable m) => a -> MuContext m #else mkGenericContext :: (Monad m, Data a, Typeable1 m) => a -> MuContext m #endif-mkGenericContext val = toGenTemp val ~> convertGenTempToContext-    +mkGenericContext val = toGenTemp id defaultExt val ~> convertGenTempToContext++{-| ++Like 'mkGenericContext', but apply the first function to record field+names when constructing the context. The second function is used to+constructing values for context from datatypes that are nor supported+as primitives in the library. The resulting value can be accessed+using the @.DatatypeName@ field:++@+\{\-\# LANGUAGE DeriveDataTypeable \#\-\}+\{\-\# LANGUAGE FlexibleInstances \#\-\}+\{\-\# LANGUAGE ScopedTypeVariables \#\-\}+\{\-\# LANGUAGE StandaloneDeriving \#\-\}+\{\-\# LANGUAGE TypeSynonymInstances \#\-\}+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 Test = Test {n::Int, m::Decimal} deriving (Data, Typeable)+deriving instance Data Decimal++val :: Test+val = Test 1 (Decimal 3 1500)++q :: Ext+q = defaultExt \`extQ\` (\(i::Decimal) -> "A decimal: " ++ show i)++r "m" = "moo"+r x   = x++example :: IO TL.Text+example = hastacheStr defaultConfig+                      (encodeStr template)+                      (mkGenericContext' r q val)++template = concat [ +     "{{n}}\\n",+     "{{moo.Decimal}}"+     ] ++main = example >>= TL.putStrLn+@++Result:++@+1+A decimal: 1.500+@++-}+#if MIN_VERSION_base(4,7,0)+mkGenericContext' :: (Monad m, Data a, Typeable m) +                  => (String -> String) -> Ext -> a -> MuContext m+#else+mkGenericContext' :: (Monad m, Data a, Typeable1 m) +                  => (String -> String) -> Ext -> a -> MuContext m+#endif+mkGenericContext' f ext val = toGenTemp f ext val ~> convertGenTempToContext+ data TD m =        TSimple (MuType m)      | TObj [(String, TD m)] @@ -215,63 +300,81 @@     deriving (Show)  #if MIN_VERSION_base(4,7,0)-toGenTemp :: (Data a, Monad m, Typeable m) => a -> TD m+toGenTemp :: (Data a, Monad m, Typeable m) +          => (String -> String) -> Ext -> a -> TD m #else-toGenTemp :: (Data a, Monad m, Typeable1 m) => a -> TD m+toGenTemp :: (Data a, Monad m, Typeable1 m) +          => (String -> String) -> Ext -> a -> TD m #endif-toGenTemp a = TObj $ conName : zip fields (gmapQ procField a)+toGenTemp f g a = TObj $ conName : zip fields (gmapQ (procField f g) a)      where-    fields = toConstr a ~> constrFields-    conName = (toConstr a ~> showConstr, MuBool True ~> TSimple)+    fields = toConstr a ~> constrFields ~> map f+    conName = (toConstr a ~> showConstr, TSimple . MuVariable $ g a)  #if MIN_VERSION_base(4,7,0)-procField :: (Data a, Monad m, Typeable m) => a -> TD m+procField :: (Data a, Monad m, Typeable m) +          => (String -> String) -> Ext -> a -> TD m #else-procField :: (Data a, Monad m, Typeable1 m) => a -> TD m+procField :: (Data a, Monad m, Typeable1 m) +          => (String -> String) -> Ext -> a -> TD m #endif-procField = -    obj-    `ext1Q` list-    `extQ` (\(i::String)            -> MuVariable (encodeStr i) ~> TSimple)-    `extQ` (\(i::Char)              -> MuVariable i ~> TSimple)-    `extQ` (\(i::Double)            -> MuVariable i ~> TSimple)-    `extQ` (\(i::Float)             -> MuVariable i ~> TSimple)-    `extQ` (\(i::Int)               -> MuVariable i ~> TSimple)-    `extQ` (\(i::Int8)              -> MuVariable i ~> TSimple)-    `extQ` (\(i::Int16)             -> MuVariable i ~> TSimple)-    `extQ` (\(i::Int32)             -> MuVariable i ~> TSimple)-    `extQ` (\(i::Int64)             -> MuVariable i ~> TSimple)-    `extQ` (\(i::Integer)           -> MuVariable i ~> TSimple)-    `extQ` (\(i::Word)              -> MuVariable i ~> TSimple)-    `extQ` (\(i::Word8)             -> MuVariable i ~> TSimple)-    `extQ` (\(i::Word16)            -> MuVariable i ~> TSimple)-    `extQ` (\(i::Word32)            -> MuVariable i ~> TSimple)-    `extQ` (\(i::Word64)            -> MuVariable i ~> TSimple)-    `extQ` (\(i::BS.ByteString)     -> MuVariable i ~> TSimple)-    `extQ` (\(i::LBS.ByteString)    -> MuVariable i ~> TSimple)-    `extQ` (\(i::T.Text)            -> MuVariable i ~> TSimple)-    `extQ` (\(i::TL.Text)           -> MuVariable i ~> TSimple)-    `extQ` (\(i::Bool)              -> MuBool i     ~> TSimple)+procField f g a = +    case res a of+      TUnknown -> TSimple . MuVariable . g $ a          +      b        -> b+  where+    res = obj+        `ext1Q` list+        `extQ` (\(i::String)            -> MuVariable (encodeStr i) ~> TSimple)+        `extQ` (\(i::Char)              -> MuVariable i ~> TSimple)+        `extQ` (\(i::Double)            -> MuVariable i ~> TSimple)+        `extQ` (\(i::Float)             -> MuVariable i ~> TSimple)+        `extQ` (\(i::Int)               -> MuVariable i ~> TSimple)+        `extQ` (\(i::Int8)              -> MuVariable i ~> TSimple)+        `extQ` (\(i::Int16)             -> MuVariable i ~> TSimple)+        `extQ` (\(i::Int32)             -> MuVariable i ~> TSimple)+        `extQ` (\(i::Int64)             -> MuVariable i ~> TSimple)+        `extQ` (\(i::Integer)           -> MuVariable i ~> TSimple)+        `extQ` (\(i::Word)              -> MuVariable i ~> TSimple)+        `extQ` (\(i::Word8)             -> MuVariable i ~> TSimple)+        `extQ` (\(i::Word16)            -> MuVariable i ~> TSimple)+        `extQ` (\(i::Word32)            -> MuVariable i ~> TSimple)+        `extQ` (\(i::Word64)            -> MuVariable i ~> TSimple)+        `extQ` (\(i::BS.ByteString)     -> MuVariable i ~> TSimple)+        `extQ` (\(i::LBS.ByteString)    -> MuVariable i ~> TSimple)+        `extQ` (\(i::T.Text)            -> MuVariable i ~> TSimple)+        `extQ` (\(i::TL.Text)           -> MuVariable i ~> TSimple)+        `extQ` (\(i::Bool)              -> MuBool i     ~> TSimple)+        `extQ` (\()                     -> MuVariable () ~> TSimple)+        `extQ` (\(i::Version)           -> MuVariable i ~> TSimple)+        `extQ` muLambdaTT+        `extQ` muLambdaTTL+        `extQ` muLambdaTLTL+        `extQ` muLambdaBSBS+        `extQ` muLambdaSS+        `extQ` muLambdaBSLBS -    `extQ` muLambdaTT-    `extQ` muLambdaTTL-    `extQ` muLambdaTLTL-    `extQ` muLambdaBSBS-    `extQ` muLambdaSS-    `extQ` muLambdaBSLBS-    -    `extQ` muLambdaMTT-    `extQ` muLambdaMTTL-    `extQ` muLambdaMTLTL-    `extQ` muLambdaMBSBS-    `extQ` muLambdaMSS-    `extQ` muLambdaMBSLBS-    where+        `extQ` muLambdaMTT+        `extQ` muLambdaMTTL+        `extQ` muLambdaMTLTL+        `extQ` muLambdaMBSBS+        `extQ` muLambdaMSS+        `extQ` muLambdaMBSLBS++        `ext1Q` muMaybe+        `ext2Q` muEither +     obj a = case dataTypeRep (dataTypeOf a) of-        AlgRep (_:_) -> toGenTemp a+        AlgRep (_:_) -> toGenTemp f g a         _ -> TUnknown-    list a = map procField a ~> TList+    list a = map (procField f g) a ~> TList +    muMaybe Nothing = TSimple MuNothing+    muMaybe (Just a) = TList [procField f g a]++    muEither (Left a) = procField f g a+    muEither (Right b) = procField f g b+     muLambdaTT :: (T.Text -> T.Text) -> TD m     muLambdaTT f = MuLambda f ~> TSimple @@ -302,7 +405,7 @@      muLambdaMTTL :: (T.Text -> m TL.Text) -> TD m     muLambdaMTTL f = MuLambdaM f ~> TSimple-        +     muLambdaMBSBS :: (BS.ByteString -> m BS.ByteString) -> TD m     muLambdaMBSBS f = MuLambdaM (f . T.encodeUtf8) ~> TSimple 
hastache.cabal view
@@ -1,5 +1,5 @@ name:            hastache-version:         0.6.0+version:         0.6.1 license:         BSD3 license-file:    LICENSE category:        Text@@ -24,19 +24,34 @@     README.md     ChangeLog +executable mkReadme+  main-is: mkReadme.hs+  build-depends: hastache, process,+    base >=4 && <4.9+    ,bytestring+    ,mtl+    ,transformers+    ,directory+    ,filepath+    ,text+    ,containers+    ,syb+    ,blaze-builder+    ,ieee754++ library   exposed-modules:     Text.Hastache     Text.Hastache.Context    build-depends:-    base == 4.*+    base >=4 && <4.9     ,bytestring     ,mtl     ,transformers     ,directory     ,filepath-    ,utf8-string     ,text     ,containers     ,syb@@ -54,7 +69,7 @@    build-depends:     hastache-   ,base == 4.*+   ,base >=4 && <4.9    ,directory    ,mtl    ,HUnit
+ mkReadme.hs view
@@ -0,0 +1,50 @@+#!/usr/local/bin/runhaskell+import Text.Hastache +import Text.Hastache.Context+import qualified Data.Text.Lazy as TL+import qualified Data.Text.Lazy.IO as TL+import System.Process (readProcess)+import System.Directory (setCurrentDirectory, getCurrentDirectory)+import System.FilePath ((</>))+import Data.List (span, lines, intersperse)+import Data.Char (isSpace)++main = do +    res <- hastacheFile defaultConfig "README.md.ha"+        (mkStrContext context)+    TL.writeFile "README.md" res +    where +    context "example" = MuLambdaM $ \fn -> do+        cd <- setExampleDir+        fc <- readFile $ exampleFile fn+        let { forTC = case span (\t -> trim t /= be) (lines $ trim fc) of+            (a,[]) -> a+            (_,a) -> drop 1 a }+        let explText = concat $ intersperse "\n" forTC+        +        setCurrentDirectory cd+        return $ concat [+              "```haskell\n"+            , explText+            , "\n```"+            ]++    context "runExample" = MuLambdaM $ \fn -> do+        cd <- setExampleDir+        explRes <- readProcess "runhaskell" [exampleFile fn] []+        setCurrentDirectory cd+        return $ concat [+              "```\n"+            , trim explRes+            , "\n```"+            ]++    be = "-- begin example"+    setExampleDir = do+        cd <- getCurrentDirectory+        setCurrentDirectory $ cd </> "examples"+        return cd+    exampleFile fn = decodeStr fn ++ ".hs"++trim :: String -> String+trim = f . f where f = reverse . dropWhile isSpace
tests/test.hs view
@@ -299,7 +299,8 @@     someMuLambdaBS      :: BS.ByteString -> BS.ByteString,     someMuLambdaS       :: String -> String,     someMuLambdaMBS     :: BS.ByteString -> IO BS.ByteString,-    someMuLambdaMS      :: String -> IO String+    someMuLambdaMS      :: String -> IO String,+    someEitherValue     :: Either String Int     }     deriving (Data, Typeable) @@ -329,6 +330,7 @@         \{{#someMuLambdaS}}upper{{/someMuLambdaS}}\n\         \{{#someMuLambdaMBS}}reverse in IO lambda{{/someMuLambdaMBS}}\n\         \{{#someMuLambdaMS}}upper in IO lambda{{/someMuLambdaMS}}\n\+        \{{someEitherValue}}\n\         \text 2\n\         \"     context = SomeData {@@ -341,7 +343,8 @@         someMuLambdaBS = BS.reverse,         someMuLambdaS = map toUpper,         someMuLambdaMBS = return . BS.reverse,-        someMuLambdaMS = return . map toUpper+        someMuLambdaMS = return . map toUpper,+        someEitherValue = Right 123         }      testRes = "\@@ -362,6 +365,7 @@         \UPPER\n\         \adbmal OI ni esrever\n\         \UPPER IN IO LAMBDA\n\+        \123\n\         \text 2\n\         \" @@ -430,6 +434,37 @@         \Nested variable : NESTED_TWO\n\         \" +-- Nested generic context with polymorphic datatypes+data Person = Person { personName :: String } deriving (Data, Typeable)+data Info = Info { person :: Maybe Person } deriving (Data, Typeable)+data Infos = Infos { infos :: [Info] } deriving (Data, Typeable)++nestedPolyGenericContextTest = do+    res <- hastacheStr defaultConfig (encodeStr template) context+    assertEqualStr resCorrectness (decodeStrLT res) testRes+    where+    datum = Infos [Info Nothing, Info $ Just $ Person "Dude", Info $ Just $ Person "Dude2"]+    template = "Greetings: {{#infos}}{{#person}}Hello, {{personName}}!\n{{/person}}{{/infos}}"+    context = mkGenericContext datum+    testRes = "Greetings: Hello, Dude!\nHello, Dude2!\n"++-- Generic context with custom extension+data MyData = MyData Int deriving (Data, Typeable)+data TestInfo = TestInfo {n::Int,m::MyData} deriving (Data, Typeable)++testExt :: Ext+testExt = defaultExt `extQ` (\(MyData i) -> "Data " ++ show i)++genericExtTest = do+    res <- hastacheStr defaultConfig (encodeStr template) context+    assertEqualStr resCorrectness (decodeStrLT res) testRes+    where+    datum = TestInfo 1 (MyData 0)+    template = "{{n}}\n{{m.MyData}}"+    context = mkGenericContext' id testExt datum+    testRes = "1\nData 0"++ -- Accessing array item by index arrayItemsTest = do     res <- hastacheStr defaultConfig (encodeStr template)@@ -503,7 +538,27 @@         \"     testRes = "42" +-- Context composition+compositionTest = do+    res <- hastacheStr defaultConfig (encodeStr template) $+        mkGenericContext context +        `composeCtx` mempty+        `composeCtx` mkStrContext context2 +    assertEqualStr resCorrectness (decodeStrLT res) testRes+    where+    context  = Heroes [Good 4, Evil 2]+    context2 "Ugly" = MuVariable (3::Int)+    context2 _      = MuNothing+    template = "\+        \{{#heroes}}\+        \{{#Good}}{{goodness}}{{/Good}}\+        \{{#Evil}}{{evilness}}{{/Evil}}\+        \{{#Ugly}}{{Ugly}}{{/Ugly}}\+        \{{/heroes}}\+        \"+    testRes = "4323"+ tests = TestList [       TestLabel "Comments test" (TestCase commentsTest)     , TestLabel "Variables test" (TestCase variablesTest)@@ -517,13 +572,18 @@     , TestLabel "Partials test" (TestCase partialsTest)     , TestLabel "Generic context test" (TestCase genericContextTest)     , TestLabel "Multiple constructors in a generic context"-        (TestCase multipleConstrTest)+        (TestCase multipleConstrTest)         , TestLabel "Nested context test" (TestCase nestedContextTest)     , TestLabel "Nested generic context test"                                         (TestCase nestedGenericContextTest)++    , TestLabel "Nested generic context with polymorphic datatypes test"+                                        (TestCase nestedPolyGenericContextTest)     , TestLabel "Accessing array item by index" (TestCase arrayItemsTest)     , TestLabel "Accessing array item by index (generic version)"         (TestCase arrayItemsTestGeneric)+    , TestLabel "Composing contexts" (TestCase compositionTest)+    , TestLabel "Generic contexts with custom extensions" (TestCase genericExtTest)     ]  main = do