diff --git a/larceny.cabal b/larceny.cabal
index 9686321..48679d2 100644
--- a/larceny.cabal
+++ b/larceny.cabal
@@ -23,6 +23,7 @@ library
, Web.Larceny.Internal
, Web.Larceny.Types
, Web.Larceny.Fills
+ , Web.Larceny.Legacy
other-extensions: OverloadedStrings
build-depends: base >=4.8 && <5
, containers >=0.5 && <0.6
diff --git a/src/Web/Larceny.hs b/src/Web/Larceny.hs
index 9742f4d..1ea9b6d 100644
--- a/src/Web/Larceny.hs
+++ b/src/Web/Larceny.hs
@@ -81,7 +81,8 @@ module Web.Larceny ( Blank(..)
, a
, (%)
, parse
- , parseWithOverrides) where
+ , parseWithOverrides
+ , LarcenyState(..)) where
import Control.Monad (filterM)
import Control.Monad.State (evalStateT)
@@ -130,7 +131,9 @@ renderWith l sub s = renderRelative l sub s []
renderRelative :: Library s -> Substitutions s -> s -> Path -> Path -> IO (Maybe Text)
renderRelative l sub s givenPath targetPath =
case findTemplate l givenPath targetPath of
- (pth, Just (Template run)) -> Just <$> evalStateT (run pth sub l) s
+ (pth, Just (Template run)) ->
+ let larcenyState = LarcenyState pth sub l defaultOverrides print s in
+ Just <$> evalStateT (run sub) larcenyState
(_, Nothing) -> return Nothing
-- | Load all the templates in some directory into a Library.
diff --git a/src/Web/Larceny/Fills.hs b/src/Web/Larceny/Fills.hs
index 107339b..00f42b8 100644
--- a/src/Web/Larceny/Fills.hs
+++ b/src/Web/Larceny/Fills.hs
@@ -17,7 +17,8 @@ module Web.Larceny.Fills ( textFill
, (%)) where
import Control.Exception
-import Control.Monad.State (StateT)
+import Control.Monad.State (StateT, get)
+import Control.Monad.Trans (liftIO)
import qualified Data.Map as M
import Data.Maybe (fromMaybe)
import Data.Text (Text)
@@ -100,8 +101,7 @@ rawTextFill t = rawTextFill' (return t)
-- textFill' getTextFromDatabase
-- @
textFill' :: StateT s IO Text -> Fill s
-textFill' t = Fill $ \_m _t _l -> HE.text <$> t
-
+textFill' t = Fill $ \_a _t -> HE.text <$> toLarcenyState t
-- | Use state or IO, then fill in some text.
--
-- @
@@ -109,7 +109,7 @@ textFill' t = Fill $ \_m _t _l -> HE.text <$> t
-- textFill' getTextFromDatabase
-- @
rawTextFill' :: StateT s IO Text -> Fill s
-rawTextFill' t = Fill $ \_m _t _l -> t
+rawTextFill' t = Fill $ \_a _t -> toLarcenyState t
-- | Create substitutions for each element in a list and fill the child nodes
-- with those substitutions.
@@ -124,17 +124,17 @@ rawTextFill' t = Fill $ \_m _t _l -> t
mapSubs :: (a -> Substitutions s)
-> [a]
-> Fill s
-mapSubs f xs = Fill $ \_attrs (pth, tpl) lib ->
- T.concat <$> mapM (\n -> runTemplate tpl pth (f n) lib) xs
+mapSubs f xs = Fill $ \_attrs tpl -> do
+ T.concat <$> mapM (\n -> runTemplate tpl (f n)) xs
-- | Create substitutions for each element in a list (using IO/state if
-- needed) and fill the child nodes with those substitutions.
mapSubs' :: (a -> StateT s IO (Substitutions s)) -> [a] -> Fill s
mapSubs' f xs = Fill $
- \_m (pth, tpl) lib ->
+ \_m tpl -> do
T.concat <$> mapM (\x -> do
- s' <- f x
- runTemplate tpl pth s' lib) xs
+ s' <- toLarcenyState $ f x
+ runTemplate tpl s') xs
-- | Fill in the child nodes of the blank with substitutions already
-- available.
@@ -182,8 +182,8 @@ fillChildrenWith' m = maybeFillChildrenWith' (Just <$> m)
-- > Bonnie Thunders
maybeFillChildrenWith :: Maybe (Substitutions s) -> Fill s
maybeFillChildrenWith Nothing = textFill ""
-maybeFillChildrenWith (Just s) = Fill $ \_s (pth, Template tpl) l ->
- tpl pth s l
+maybeFillChildrenWith (Just s) = Fill $ \_attrs tpl -> do
+ runTemplate tpl s
-- | Use state and IO and maybe fill in with some substitutions.
--
@@ -198,11 +198,11 @@ maybeFillChildrenWith (Just s) = Fill $ \_s (pth, Template tpl) l ->
--
-- > Bonnie Thunders
maybeFillChildrenWith' :: StateT s IO (Maybe (Substitutions s)) -> Fill s
-maybeFillChildrenWith' sMSubs = Fill $ \_s (pth, Template tpl) l -> do
- mSubs <- sMSubs
+maybeFillChildrenWith' sMSubs = Fill $ \_s (Template tpl) -> do
+ mSubs <- toLarcenyState sMSubs
case mSubs of
Nothing -> return ""
- Just s -> tpl pth s l
+ Just s -> tpl s
-- | Use attributes from the the blank as arguments to the fill.
--
@@ -223,8 +223,8 @@ maybeFillChildrenWith' sMSubs = Fill $ \_s (pth, Template tpl) l -> do
useAttrs :: (Attributes -> k -> Fill s)
-> k
-> Fill s
-useAttrs k fill= Fill $ \atrs (pth, tpl) lib ->
- unFill (k atrs fill) atrs (pth, tpl) lib
+useAttrs k fill= Fill $ \atrs tpl ->
+ unFill (k atrs fill) atrs tpl
-- | Prepend `a` to the name of an attribute to pass the value of that
-- attribute to the fill.
diff --git a/src/Web/Larceny/Internal.hs b/src/Web/Larceny/Internal.hs
index f71cbe5..b0fe57c 100644
--- a/src/Web/Larceny/Internal.hs
+++ b/src/Web/Larceny/Internal.hs
@@ -1,13 +1,13 @@
-{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE OverloadedStrings #-}
module Web.Larceny.Internal ( findTemplate
, parse
, parseWithOverrides) where
import Control.Exception
-import Lens.Micro
+import Lens.Micro ((.~))
+import Control.Monad.State (MonadState, modify, get)
import Control.Monad.Trans (liftIO)
-import Control.Monad.State (MonadState, StateT, evalStateT, runStateT, get, modify)
import qualified Data.HashSet as HS
import qualified Data.Map as M
import Data.Maybe (fromMaybe)
@@ -18,27 +18,17 @@ import qualified Data.Text.Lazy as LT
import qualified Text.HTML.DOM as D
import qualified Text.XML as X
------------
-import Web.Larceny.Types
import Web.Larceny.Fills
import Web.Larceny.Html (html5Nodes, html5SelfClosingNodes)
import Web.Larceny.Svg (svgNodes)
-
--- | Turn lazy text into templates.
-parse :: LT.Text -> Template s
-parse = parseWithOverrides defaultOverrides
-
--- | Use overrides when parsing a template.
-parseWithOverrides :: Overrides -> LT.Text -> Template s
-parseWithOverrides o t =
- let textWithoutDoctype = LT.replace "" "" t
- (X.Document _ (X.Element _ _ nodes) _) = D.parseLT ("
" <> textWithoutDoctype <> "
")
- in mk o $! map (toLarcenyNode o) nodes
+import Web.Larceny.Types
-- | Phases of the template parsing/rendering process: 1. Parse the document
-- into HTML (or really, XML) nodes 2. Turn those nodes into Larceny nodes,
-- which encodes more information about the elements, including prefix and
-- whether the node is a regular HTML node, a special Larceny element, or a
-- Larceny blank. 3. Render each node into Text according to its node type.
+
data Node = NodeElement Element
| NodeContent Text
| NodeComment Text
@@ -49,6 +39,18 @@ data Element = PlainElement Name Attributes [Node]
| BlankElement Name Attributes [Node]
| DoctypeElement
+-- | Turn lazy text into templates.
+parse :: LT.Text -> Template s
+parse = parseWithOverrides defaultOverrides
+
+-- | Use overrides when parsing a template.
+parseWithOverrides :: Overrides -> LT.Text -> Template s
+parseWithOverrides o t =
+ let textWithoutDoctype = LT.replace "" "" t
+ (X.Document _ (X.Element _ _ nodes) _) = D.parseLT ("" <> textWithoutDoctype <> "
")
+ in mk $! map (toLarcenyNode o) nodes
+
+
toLarcenyName :: X.Name -> Name
toLarcenyName (X.Name tn _ _) =
case T.stripPrefix "l:" tn of
@@ -93,25 +95,13 @@ toLarcenyNode _ (X.NodeComment c) = NodeComment c
toLarcenyNode _ (X.NodeInstruction _) = NodeContent ""
-- | Turn HTML nodes and overrides into templates.
-mk :: Overrides -> [Node] -> Template s
-mk o = f
+mk :: [Node] -> Template s
+mk = f
where f nodes =
- Template $ \pth m l ->
- let pc = ProcessContext pth m l o f nodes in
- do s <- get
- T.concat <$> toUserState (pc s) (process nodes)
-
-toProcessState :: StateT s IO a -> StateT (ProcessContext s) IO a
-toProcessState f =
- do pc <- get
- (result, s') <- liftIO $ runStateT f (_pcState pc)
- pcState .= s'
- return result
-
-toUserState :: ProcessContext s -> StateT (ProcessContext s) IO a -> StateT s IO a
-toUserState pc f =
- do s <- get
- liftIO $ evalStateT f (pc { _pcState = s })
+ Template $ \m ->
+ do modify (lSubs .~ m)
+ -- lPath .= pth
+ T.concat <$> process nodes
fillIn :: Blank -> Substitutions s -> Fill s
fillIn tn m = fromMaybe (fallbackFill tn m) (M.lookup tn m)
@@ -120,45 +110,21 @@ fallbackFill :: Blank -> Substitutions s -> Fill s
fallbackFill FallbackBlank m = fromMaybe (textFill "") (M.lookup FallbackBlank m)
fallbackFill (Blank tn) m =
let fallback = fromMaybe (textFill "") (M.lookup FallbackBlank m) in
- Fill $ \attr (pth, tpl) lib ->
- do liftIO $ putStrLn ("Larceny: Missing fill for blank " <> show tn <> " in template " <> show pth)
- unFill fallback attr (pth, tpl) lib
-
-data ProcessContext s = ProcessContext { _pcPath :: Path
- , _pcSubs :: Substitutions s
- , _pcLib :: Library s
- , _pcOverrides :: Overrides
- , _pcMk :: [Node] -> Template s
- , _pcNodes :: [Node]
- , _pcState :: s }
-
-infix 4 .=
-(.=) :: MonadState s m => ASetter s s a b -> b -> m ()
-l .= b = modify (l .~ b)
-{-# INLINE (.=) #-}
-
-pcSubs :: Lens' (ProcessContext s) (Substitutions s)
-pcSubs = lens _pcSubs (\pc s -> pc { _pcSubs = s })
-
-pcNodes :: Lens' (ProcessContext s) [Node]
-pcNodes = lens _pcNodes (\pc n -> pc { _pcNodes = n })
-
-pcState :: Lens' (ProcessContext s) s
-pcState = lens _pcState (\pc s -> pc { _pcState = s })
-
-type ProcessT s = StateT (ProcessContext s) IO [Text]
+ Fill $ \attr tpl ->
+ do st <- get
+ let pth = _lPath st
+ liftIO $ putStrLn ("Larceny: Missing fill for blank " <> show tn <> " in template " <> show pth)
+ unFill fallback attr tpl
add :: Substitutions s -> Template s -> Template s
add mouter tpl =
- Template (\pth minner l -> runTemplate tpl pth (minner `M.union` mouter) l)
+ Template (\minner -> runTemplate tpl (minner `M.union` mouter))
-process :: [Node] -> ProcessT s
+process :: [Node] -> LarcenyM s [Text]
process [] = return []
process (NodeElement (BindElement atr kids):nextNodes) = do
- pcNodes .= nextNodes
- processBind atr kids
+ processBind atr kids nextNodes
process (currentNode:nextNodes) = do
- pcNodes .= nextNodes
processedNode <-
case currentNode of
NodeElement DoctypeElement -> return [""]
@@ -182,12 +148,12 @@ process (currentNode:nextNodes) = do
processPlain :: Name ->
Attributes ->
[Node] ->
- ProcessT s
+ LarcenyM s [Text]
processPlain tagName atr kids = do
pc <- get
atrs <- attrsToText atr
processed <- process kids
- return $ tagToText (_pcOverrides pc) tagName atrs processed
+ return $ tagToText (_lOverrides pc) tagName atrs processed
selfClosing :: Overrides -> HS.HashSet Text
selfClosing (Overrides _ _ sc) =
@@ -206,7 +172,7 @@ tagToText overrides (Name mPf name) atrs processed =
++ processed
++ ["" <> prefix <> name <> ">"]
-attrsToText :: Attributes -> StateT (ProcessContext s) IO Text
+attrsToText :: Attributes -> LarcenyM s Text
attrsToText attrs =
T.concat <$> mapM attrToText (M.toList attrs)
where attrToText (k,v) = do
@@ -217,7 +183,7 @@ attrsToText attrs =
toText (k, "") = " " <> k
toText (k, v) = " " <> k <> "=\"" <> T.strip v <> "\""
-fillAttrs :: Attributes -> StateT (ProcessContext s) IO Attributes
+fillAttrs :: Attributes -> LarcenyM s Attributes
fillAttrs attrs = M.fromList <$> mapM fill (M.toList attrs)
where fill p = do
let (unboundKeys, unboundValues) = eUnboundAttrs p
@@ -225,12 +191,11 @@ fillAttrs attrs = M.fromList <$> mapM fill (M.toList attrs)
vals <- T.concat <$> mapM fillAttr unboundValues
return (keys, vals)
-fillAttr :: Either Text Blank -> StateT (ProcessContext s) IO Text
+fillAttr :: Either Text Blank -> LarcenyM s Text
fillAttr eBlankText =
- do (ProcessContext pth m l _ mko _ _) <- get
- toProcessState $
- case eBlankText of
- Right hole -> unFill (fillIn hole m) mempty (pth, mko []) l
+ do m <- _lSubs <$> get
+ case eBlankText of
+ Right hole -> unFill (fillIn hole m) mempty (mk [])
Left text -> return text
-- Look up the Fill for the hole. Apply the Fill to a map of
@@ -239,24 +204,25 @@ fillAttr eBlankText =
processBlank :: Text ->
Attributes ->
[Node] ->
- ProcessT s
+ LarcenyM s [Text]
processBlank tagName atr kids = do
- (ProcessContext pth m l _ mko _ _) <- get
+ m <- _lSubs <$> get
filled <- fillAttrs atr
- sequence [ toProcessState $ unFill (fillIn (Blank tagName) m)
+ sequence [ unFill (fillIn (Blank tagName) m)
filled
- (pth, add m (mko kids)) l]
+ (add m (mk kids))]
processBind :: Attributes ->
[Node] ->
- ProcessT s
-processBind atr kids = do
- (ProcessContext pth m l _ mko nodes _) <- get
+ [Node] ->
+ LarcenyM s [Text]
+processBind atr kids nextNodes = do
+ m <- _lSubs <$> get
let tagName = atr M.! "tag"
- newSubs = subs [(tagName, Fill $ \_a _t _l ->
- runTemplate (mko kids) pth m l)]
- pcSubs .= newSubs `M.union` m
- process nodes
+ newSubs = subs [(tagName, Fill $ \_a _t ->do
+ runTemplate (mk kids) m)]
+ modify (lSubs .~ newSubs `M.union` m)
+ process nextNodes
-- Look up the template that's supposed to be applied in the library,
-- create a substitution for the content hole using the child elements
@@ -264,15 +230,16 @@ processBind atr kids = do
-- combined with outer substitution and the library.
processApply :: Attributes ->
[Node] ->
- ProcessT s
+ LarcenyM s [Text]
processApply atr kids = do
- (ProcessContext pth m l _ mko _ _) <- get
+ (LarcenyState pth m l _ _ _) <- get
filledAttrs <- fillAttrs atr
let (absolutePath, tplToApply) = findTemplateFromAttrs pth l filledAttrs
- contentTpl <- toProcessState $ runTemplate (mko kids) pth m l
+ contentTpl <- runTemplate (mk kids) m
let contentSub = subs [("apply-content",
rawTextFill contentTpl)]
- sequence [ toProcessState $ runTemplate tplToApply absolutePath (contentSub `M.union` m) l ]
+ modify (lPath .~ absolutePath)
+ sequence [ runTemplate tplToApply (contentSub `M.union` m) ]
findTemplateFromAttrs :: Path ->
Library s ->
diff --git a/src/Web/Larceny/Legacy.hs b/src/Web/Larceny/Legacy.hs
new file mode 100644
index 0000000..9700850
--- /dev/null
+++ b/src/Web/Larceny/Legacy.hs
@@ -0,0 +1,92 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Web.Larceny.Legacy ( textFill
+ , textFill'
+ , rawTextFill
+ , rawTextFill'
+ , mapSubs
+ , mapSubs'
+ , fillChildren
+ , fillChildrenWith
+ , fillChildrenWith'
+ , maybeFillChildrenWith
+ , maybeFillChildrenWith'
+ , ifFill
+ , useAttrs
+ , a
+ , (%)) where
+
+import Control.Monad.State (StateT)
+import Data.Maybe (fromMaybe)
+import Data.Text (Text)
+import qualified Data.Text as T
+import qualified HTMLEntities.Text as HE
+------------
+import Web.Larceny.Fills (a, (%))
+import Web.Larceny.Types
+
+ifFill :: Fill s
+ifFill =
+ useAttrs (a "condition" % a "exists") ifFill'
+ where ifFill' :: Maybe Bool -> Maybe Text -> Fill s
+ ifFill' mCondition mExisting =
+ let condition = fromMaybe True mCondition
+ existing = fromMaybe "exist" mExisting
+ bool = condition && existing /= ""
+ thenElseSubs = subs [("then", thenFill bool)
+ ,("else", thenFill (not bool))] in
+ fillChildrenWith thenElseSubs
+ thenFill True = fillChildren
+ thenFill False = textFill ""
+
+textFill :: Text -> Fill s
+textFill t = textFill' (return t)
+
+rawTextFill :: Text -> Fill s
+rawTextFill t = rawTextFill' (return t)
+
+textFill' :: StateT s IO Text -> Fill s
+textFill' t = Fill $ \_a _t -> HE.text <$> toLarcenyState t
+
+rawTextFill' :: StateT s IO Text -> Fill s
+rawTextFill' t = Fill $ \_a _t -> toLarcenyState t
+
+mapSubs :: (a -> Substitutions s)
+ -> [a]
+ -> Fill s
+mapSubs f xs = Fill $ \_attrs tpl -> do
+ T.concat <$> mapM (\n -> runTemplate tpl (f n)) xs
+
+mapSubs' :: (a -> StateT s IO (Substitutions s)) -> [a] -> Fill s
+mapSubs' f xs = Fill $
+ \_m tpl -> do
+ T.concat <$> mapM (\x -> do
+ s' <- toLarcenyState $ f x
+ runTemplate tpl s') xs
+
+fillChildren :: Fill s
+fillChildren = fillChildrenWith mempty
+
+fillChildrenWith :: Substitutions s -> Fill s
+fillChildrenWith m = maybeFillChildrenWith (Just m)
+
+fillChildrenWith' :: StateT s IO (Substitutions s) -> Fill s
+fillChildrenWith' m = maybeFillChildrenWith' (Just <$> m)
+
+maybeFillChildrenWith :: Maybe (Substitutions s) -> Fill s
+maybeFillChildrenWith Nothing = textFill ""
+maybeFillChildrenWith (Just s) = Fill $ \_attrs tpl -> do
+ runTemplate tpl s
+
+maybeFillChildrenWith' :: StateT s IO (Maybe (Substitutions s)) -> Fill s
+maybeFillChildrenWith' sMSubs = Fill $ \_s (Template tpl) -> do
+ mSubs <- toLarcenyState sMSubs
+ case mSubs of
+ Nothing -> return ""
+ Just s -> tpl s
+
+useAttrs :: (Attributes -> k -> Fill s)
+ -> k
+ -> Fill s
+useAttrs k fill= Fill $ \atrs tpl ->
+ unFill (k atrs fill) atrs tpl
diff --git a/src/Web/Larceny/Types.hs b/src/Web/Larceny/Types.hs
index d9ed1b0..094fc82 100644
--- a/src/Web/Larceny/Types.hs
+++ b/src/Web/Larceny/Types.hs
@@ -14,10 +14,20 @@ module Web.Larceny.Types ( Blank(..)
, defaultOverrides
, FromAttribute(..)
, AttrError(..)
- , ApplyError(..)) where
+ , ApplyError(..)
+ , LarcenyState(..)
+ , LarcenyM
+ , toLarcenyState
+ , lSubs
+ , lPath
+ , lLib
+ , lOverrides
+ , lAppState) where
import Control.Exception
-import Control.Monad.State (StateT)
+import Lens.Micro
+import Control.Monad.State (StateT, get, put, runStateT, modify, MonadState)
+import Control.Monad.Trans (liftIO)
import Data.Hashable (Hashable, hash, hashWithSalt)
import Data.Map (Map)
import qualified Data.Map as M
@@ -26,6 +36,35 @@ import Data.Text (Text)
import qualified Data.Text as T
import Text.Read (readMaybe)
+type LarcenyM s a = StateT (LarcenyState s) IO a
+
+data LarcenyState s =
+ LarcenyState { _lPath :: [Text]
+ , _lSubs :: Substitutions s
+ , _lLib :: Library s
+ , _lOverrides :: Overrides
+ , _lLogger :: (Text -> IO ())
+ , _lAppState :: s }
+
+lSubs :: Lens' (LarcenyState s) (Substitutions s)
+lSubs = lens _lSubs (\l s -> l { _lSubs = s })
+lPath :: Lens' (LarcenyState s) Path
+lPath = lens _lPath (\l s -> l { _lPath = s })
+lLib :: Lens' (LarcenyState s) (Library s)
+lLib = lens _lLib (\l s -> l { _lLib = s })
+lOverrides :: Lens' (LarcenyState s) Overrides
+lOverrides = lens _lOverrides (\ls o -> ls { _lOverrides = o })
+lAppState:: Lens' (LarcenyState s) s
+lAppState = lens _lAppState (\ls s -> ls { _lAppState = s })
+
+-- TODO: This shouldn't be necessary.
+toLarcenyState :: StateT s IO a -> LarcenyM s a
+toLarcenyState f = do
+ l <- get
+ (result, newAppState) <- liftIO $ runStateT f (_lAppState l)
+ put (l {_lAppState = newAppState })
+ return result
+
-- | Corresponds to a "blank" in the template that can be filled in
-- with some value when the template is rendered. Blanks can be tags
-- or they can be all or parts of attribute values in tags.
@@ -51,8 +90,8 @@ instance Hashable Blank where
-- from scratch.
--
-- @
--- Fill $ \attrs _tpl _lib ->
--- return $ T.pack $ show $ M.keys attrs)
+-- Fill $ \attrs _tpl ->
+-- return $ Text.intercalate " and " $ M.keys attrs)
-- @
--
-- With that Fill, a Blank like this:
@@ -61,16 +100,15 @@ instance Hashable Blank where
--
-- would be rendered as:
--
--- > ["attribute", "another"]
+-- > another and attribute
--
-- Fills (and Substitutions and Templates) have the type `StateT s IO
-- Text` in case you need templates to depend on IO actions (like
-- looking something up in a database) or store state (perhaps keeping
-- track of what's already been rendered).
newtype Fill s = Fill { unFill :: Attributes
- -> (Path, Template s)
- -> Library s
- -> StateT s IO Text }
+ -> Template s
+ -> LarcenyM s Text }
-- | The Blank's attributes, a map from the attribute name to
-- it's value.
@@ -116,10 +154,8 @@ fallbackSub fill = M.fromList [(FallbackBlank, fill)]
-- Use `loadTemplates` to load the templates from some directory
-- into a template library. Use the `render` functions to render
-- templates from a Library by path.
-newtype Template s = Template { runTemplate :: Path
- -> Substitutions s
- -> Library s
- -> StateT s IO Text }
+newtype Template s = Template { runTemplate :: Substitutions s
+ -> LarcenyM s Text }
-- | The path to a template.
type Path = [Text]
diff --git a/test/Examples.hs b/test/Examples.hs
index 0b1c4e0..2dca620 100644
--- a/test/Examples.hs
+++ b/test/Examples.hs
@@ -64,9 +64,10 @@ subst = subs [ ("site-title", textFill "Gotham Girls roster")
modifyInnerText :: (Text -> Text) -> Fill ()
modifyInnerText f = Fill $
- \_attrs (_pth, tpl) _l ->
+ \_attrs tpl ->
liftIO $ do
- t' <- evalStateT (runTemplate tpl ["default"] mempty mempty) ()
+ let larcenyState = LarcenyState ["default"] mempty mempty defaultOverrides print ()
+ t' <- evalStateT (runTemplate tpl mempty) larcenyState
return $ f t'
tplLib :: Library ()
diff --git a/test/Spec.hs b/test/Spec.hs
index 716bdd1..3dd418b 100644
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -1,18 +1,18 @@
+{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE TypeSynonymInstances #-}
import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar)
-import Control.Exception (Exception, catch, throw, try, ErrorCall(..))
-import Lens.Micro
-import Control.Monad.State (StateT (..), evalStateT, get, modify,
- runStateT)
+import Control.Exception (ErrorCall (..), Exception, catch,
+ throw, try)
+import Control.Monad.State (StateT (..), evalStateT, get,
+ runStateT, modify, MonadState)
import qualified Control.Monad.State as S
import Control.Monad.Trans (liftIO)
import qualified Data.Map as M
@@ -23,43 +23,33 @@ import qualified Data.Text as T
import qualified Data.Text.Lazy as LT
import Data.Typeable
import Examples
+import Lens.Micro
import Test.Hspec
import qualified Test.Hspec.Core.Spec as H
-import Web.Larceny
-
-infix 4 .=
-(.=) :: S.MonadState s m => ASetter s s a b -> b -> m ()
-l .= b = modify (l .~ b)
-{-# INLINE (.=) #-}
-data LarcenyState =
- LarcenyState { _lPath :: [Text]
- , _lSubs :: Substitutions ()
- , _lLib :: Library ()
- , _lOverrides :: Overrides }
-lPath :: Lens' LarcenyState [Text]
-lPath = lens _lPath (\ls p -> ls { _lPath = p })
-lSubs :: Lens' LarcenyState (Substitutions ())
-lSubs = lens _lSubs (\ls s -> ls { _lSubs = s })
-lLib :: Lens' LarcenyState (Library ())
-lLib = lens _lLib (\ls l -> ls { _lLib = l })
-lOverrides :: Lens' LarcenyState Overrides
-lOverrides = lens _lOverrides (\ls o -> ls { _lOverrides = o })
+import Web.Larceny
+import Web.Larceny.Types (lAppState, lLib, lOverrides, lPath,
+ lSubs)
-type LarcenyHspecM = StateT LarcenyHspecState IO
+type LarcenyHspecM s = StateT (LarcenyHspecState s) IO
-data LarcenyHspecState =
+data LarcenyHspecState s =
LarcenyHspecState { _hResult :: H.Result
- , _hLarcenyState :: LarcenyState }
+ , _hLarcenyState :: LarcenyState s }
+
+infix 4 .=
+(.=) :: MonadState s m => ASetter s s a b -> b -> m ()
+l .= b = modify (l .~ b)
+{-# INLINE (.=) #-}
-hResult :: Lens' LarcenyHspecState H.Result
+hResult :: Lens' (LarcenyHspecState s) H.Result
hResult = lens _hResult (\hs r -> hs { _hResult = r })
-hLarcenyState :: Lens' LarcenyHspecState LarcenyState
+hLarcenyState :: Lens' (LarcenyHspecState s) (LarcenyState s)
hLarcenyState = lens _hLarcenyState (\hs ls -> hs { _hLarcenyState = ls })
-instance H.Example (LarcenyHspecM ()) where
- type Arg (LarcenyHspecM ()) = LarcenyHspecState
+instance H.Example (LarcenyHspecM s ()) where
+ type Arg (LarcenyHspecM a ()) = LarcenyHspecState a
evaluateExample s _params actionWithToIO _progCallback =
do mv <- newEmptyMVar
actionWithToIO $ \st ->
@@ -70,15 +60,16 @@ instance H.Example (LarcenyHspecM ()) where
putMVar mv r
takeMVar mv
-withLarceny :: SpecWith LarcenyHspecState
+withLarceny :: s
+ -> SpecWith (LarcenyHspecState s)
-> Spec
-withLarceny spec' =
+withLarceny s spec' =
let larcenyHspecState =
- LarcenyHspecState H.Success (LarcenyState ["default"] mempty mempty mempty) in
+ LarcenyHspecState H.Success (LarcenyState ["default"] mempty mempty mempty print s) in
afterAll return $
before (return larcenyHspecState) spec'
-setResult :: H.Result -> LarcenyHspecM ()
+setResult :: H.Result -> LarcenyHspecM s ()
setResult r = case r of
H.Success -> hResult .= r
_ -> throw r
@@ -111,13 +102,13 @@ tpl4Output = "\
removeSpaces :: Text -> Text
removeSpaces = T.replace " " ""
-renderM :: Text -> LarcenyHspecM Text
+renderM :: Text -> LarcenyHspecM s Text
renderM templateText = do
- (LarcenyHspecState _ (LarcenyState p s l o)) <- S.get
+ (LarcenyHspecState _ ls@(LarcenyState _ s _ o _ _)) <- S.get
let tpl = parseWithOverrides o (LT.fromStrict templateText)
- liftIO $ evalStateT (runTemplate tpl p s l) ()
+ liftIO $ evalStateT (runTemplate tpl s) ls
-shouldRenderM :: Text -> Text -> LarcenyHspecM ()
+shouldRenderM :: Text -> Text -> LarcenyHspecM s ()
shouldRenderM templateText output = do
rendered <- renderM templateText
if removeSpaces rendered == removeSpaces output
@@ -125,7 +116,7 @@ shouldRenderM templateText output = do
else let msg = T.unpack $ rendered <> " doesn't match " <> output in
setResult (H.Fail Nothing msg)
-shouldRenderContainingM :: Text -> Text -> LarcenyHspecM ()
+shouldRenderContainingM :: Text -> Text -> LarcenyHspecM s ()
shouldRenderContainingM templateText excerpt = do
rendered <- renderM templateText
if excerpt `T.isInfixOf` rendered
@@ -133,7 +124,7 @@ shouldRenderContainingM templateText excerpt = do
else let msg = T.unpack $ excerpt <> " not found in " <> templateText in
setResult (H.Fail Nothing msg)
-shouldErrorM :: (Exception a, Eq a) => Text -> Selector a -> LarcenyHspecM ()
+shouldErrorM :: (Exception a, Eq a) => Text -> Selector a -> LarcenyHspecM s ()
shouldErrorM templateText p =
do hspecState <- S.get
let renderAttempt = evalStateT (renderM templateText) hspecState
@@ -160,7 +151,7 @@ main = spec
spec :: IO ()
spec = hspec $ do
- withLarceny $ do
+ withLarceny () $ do
describe "parse" $ do
it "should parse HTML into a Template" $ do
hLarcenyState.lSubs .= subst
@@ -290,7 +281,7 @@ spec = hspec $ do
\" `shouldRenderM` "Fill this in Fill this in"
it "should not let binds escape the apply-content tag" $ do
- hLarcenyState.lSubs .= fallbackSub (Fill $ \_ _ _ -> error "not found!")
+ hLarcenyState.lSubs .= fallbackSub (Fill $ \_ _ -> error "not found!")
let lib = M.fromList [(["blah"], parse "")]
hLarcenyState.lLib .= lib
" \
@@ -318,7 +309,7 @@ spec = hspec $ do
it "should allow you to write functions for fills" $ do
let subs' =
subs [("desc",
- Fill $ \m _t _l -> return $ T.take (read $ T.unpack (m M.! "length"))
+ Fill $ \m _t-> return $ T.take (read $ T.unpack (m M.! "length"))
"A really long description"
<> "...")]
hLarcenyState.lSubs .= subs'
@@ -327,8 +318,8 @@ spec = hspec $ do
it "should allow you to use IO in fills" $ do
let subs' =
subs [("desc", Fill $
- \m _t _l -> do liftIO $ putStrLn "***********\nHello World\n***********"
- return $ T.take (read $ T.unpack (m M.! "length"))
+ \m _t -> do liftIO $ putStrLn "***********\nHello World\n***********"
+ return $ T.take (read $ T.unpack (m M.! "length"))
"A really long description"
<> "...")]
hLarcenyState.lSubs .= subs'
@@ -376,11 +367,21 @@ spec = hspec $ do
`shouldRenderM` ""
it "should know what the template path is" $ do
- let fill = Fill $ \_ (p, _) _ -> return (head p)
+ let fill = Fill $ \_ _ -> do
+ st <- get
+ let p = _lPath st
+ return (head p)
hLarcenyState.lSubs .= subs [("template", fill)]
""
`shouldRenderM` ""
+ it "should work like the docs say" $ do
+ let fill = Fill $ \attrs _tpl ->
+ return $ T.intercalate " and " $ M.keys attrs
+ hLarcenyState.lSubs .= subs [("displayAttrs", fill)]
+ ""
+ `shouldRenderM` "attribute1 and attribute2"
+
describe "a large template" $ do
it "should render large HTML files" $ do
hLarcenyState.lSubs .= subst
@@ -420,9 +421,9 @@ spec = hspec $ do
doctypeTests
conditionalTests
namespaceTests
- statefulTests
+ withLarceny 0 $ statefulTests
-namespaceTests :: SpecWith LarcenyHspecState
+namespaceTests :: SpecWith (LarcenyHspecState ())
namespaceTests =
describe "namespaces" $ do
it "should assume that tags with namespaces are blanks" $ do
@@ -441,18 +442,22 @@ namespaceTests =
""
`shouldRenderM` ""
-statefulTests :: SpecWith ()
+statefulTests :: SpecWith (LarcenyHspecState Int)
statefulTests =
+ let incrementSub =
+ subs [("increment-and-print",
+ Fill $ \_ _ ->
+ do -- eek refactor later
+ s <- get
+ lAppState .= (_lAppState s + 1 :: Int)
+ s' <- get
+ let int = _lAppState s'
+ return (T.pack (show int)))] in
describe "statefulness" $ do
it "a fill should be able to affect subsequent fills" $ do
- renderWith (M.fromList [(["default"], parse "")])
- (subs [("x", Fill $ \_ _ _ ->
- do modify ((+1) :: Int -> Int)
- s <- get
- return (T.pack (show s)))])
- 0
- ["default"]
- `shouldReturn` Just "12"
+ hLarcenyState.lSubs .= incrementSub
+ "" `shouldRenderM` "12"
+
{- The following test was prompted by a bug where I refuktored the
bind tag handling to be inside the case statement in `process`.
The bind tag processor itself calls bind but doesn't return any
@@ -460,19 +465,13 @@ statefulTests =
over and over again.
-}
it "should not be affected by binds" $ do
- let tpl = "test1\
- \test2\
- \"
- renderWith (M.fromList [(["default"], parse tpl)])
- (subs [("x", Fill $ \_ _ _ ->
- do modify ((+1) :: Int -> Int)
- s <- get
- return (T.pack (show s)))])
- 0
- ["default"]
- `shouldReturn` Just "12"
-
-doctypeTests :: SpecWith LarcenyHspecState
+ hLarcenyState.lSubs .= incrementSub
+ "test1\
+ \test2\
+ \"
+ `shouldRenderM` "12"
+
+doctypeTests :: SpecWith (LarcenyHspecState ())
doctypeTests = do
describe "doctypes" $ do
it "should render blank doctypes" $ do
@@ -483,7 +482,7 @@ doctypeTests = do
"Hello world
"
`shouldRenderM` "Hello world
"
-conditionalTests :: SpecWith LarcenyHspecState
+conditionalTests :: SpecWith (LarcenyHspecState ())
conditionalTests = do
describe "conditionals" $ do
let template cond =
@@ -598,7 +597,7 @@ conditionalTests = do
template `shouldRenderM` "It is empty!"
-fallbackTests ::SpecWith LarcenyHspecState
+fallbackTests ::SpecWith (LarcenyHspecState ())
fallbackTests = do
describe "templates with missing blanks" $ do
it "should render empty text by default" $ do
@@ -610,10 +609,10 @@ fallbackTests = do
hLarcenyState.lSubs .= fallbackSub (rawTextFill "I'm a fallback.")
"missing:
" `shouldRenderM` "missing: I'm a fallback.
"
it "should allow errors to be thrown, e.g., in dev mode" $ do
- hLarcenyState.lSubs .= fallbackSub (Fill $ \_ _ _ -> error "missing blank!")
+ hLarcenyState.lSubs .= fallbackSub (Fill $ \_ _ -> error "missing blank!")
"missing:
" `shouldErrorM` (== ErrorCall "missing blank!")
-attrTests :: SpecWith LarcenyHspecState
+attrTests :: SpecWith (LarcenyHspecState ())
attrTests =
describe "useAttrs" $ do
it "should allow you to *easily* write functions for fills" $ do
@@ -634,8 +633,9 @@ attrTests =
it "should allow you use child elements" $ do
let descTplFill =
useAttrs (a"length")
- (\n -> Fill $ \_attrs (_pth, tpl) _l -> liftIO $ do
- t' <- evalStateT (runTemplate tpl ["default"] mempty mempty) ()
+ (\n -> Fill $ \_attrs tpl -> liftIO $ do
+ let larcenyState = LarcenyState ["default"] mempty mempty defaultOverrides print ()
+ t' <- evalStateT (runTemplate tpl mempty) larcenyState
return $ T.take n t' <> "...")
hLarcenyState.lSubs .= subs [ ("adverb", textFill "really")
, ("desc", descTplFill)]
@@ -674,8 +674,9 @@ attrTests =
descFunc :: Int -> Maybe Text -> Fill ()
descFunc n e = Fill $
do let ending = fromMaybe "..." e
- \_attrs (_pth, tpl) _l -> liftIO $ do
- renderedText <- evalStateT (runTemplate tpl ["default"] mempty mempty) ()
+ \_attrs tpl -> liftIO $ do
+ let larcenyState = LarcenyState ["default"] mempty mempty defaultOverrides print ()
+ renderedText <- evalStateT (runTemplate tpl mempty) larcenyState
return $ T.take n renderedText <> ending
{-# ANN module ("HLint: ignore Redundant do" :: String) #-}