From 1e781613689007832508e607d4b359898106063a Mon Sep 17 00:00:00 2001 From: Libby Horacek Date: Sun, 20 May 2018 16:29:11 -0400 Subject: [PATCH 01/14] Add app state to LarcenyState --- test/Spec.hs | 108 ++++++++++++++++++++++++++------------------------- 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/test/Spec.hs b/test/Spec.hs index 716bdd1..88d00eb 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -32,34 +32,36 @@ infix 4 .= l .= b = modify (l .~ b) {-# INLINE (.=) #-} -data LarcenyState = +data LarcenyState s = LarcenyState { _lPath :: [Text] - , _lSubs :: Substitutions () - , _lLib :: Library () - , _lOverrides :: Overrides } + , _lSubs :: Substitutions s + , _lLib :: Library s + , _lOverrides :: Overrides + , _lLogger :: (Text -> IO ()) + , _lAppState :: s } -lPath :: Lens' LarcenyState [Text] +lPath :: Lens' (LarcenyState s) [Text] lPath = lens _lPath (\ls p -> ls { _lPath = p }) -lSubs :: Lens' LarcenyState (Substitutions ()) +lSubs :: Lens' (LarcenyState s) (Substitutions s) lSubs = lens _lSubs (\ls s -> ls { _lSubs = s }) -lLib :: Lens' LarcenyState (Library ()) +lLib :: Lens' (LarcenyState s) (Library s) lLib = lens _lLib (\ls l -> ls { _lLib = l }) -lOverrides :: Lens' LarcenyState Overrides +lOverrides :: Lens' (LarcenyState s) Overrides lOverrides = lens _lOverrides (\ls o -> ls { _lOverrides = o }) -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 } -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 +72,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 +114,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 _ (LarcenyState p s l o _ st)) <- S.get let tpl = parseWithOverrides o (LT.fromStrict templateText) - liftIO $ evalStateT (runTemplate tpl p s l) () + liftIO $ evalStateT (runTemplate tpl p s l) st -shouldRenderM :: Text -> Text -> LarcenyHspecM () +shouldRenderM :: Text -> Text -> LarcenyHspecM s () shouldRenderM templateText output = do rendered <- renderM templateText if removeSpaces rendered == removeSpaces output @@ -125,7 +128,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 +136,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 +163,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 @@ -420,9 +423,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 +444,18 @@ namespaceTests = "" `shouldRenderM` "" -statefulTests :: SpecWith () +statefulTests :: SpecWith (LarcenyHspecState Int) statefulTests = 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 .= + subs [("increment-and-print", + Fill $ \_ _ _ -> + do modify ((+1) :: Int -> Int) + s <- get + return (T.pack (show s)))] + "" `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 +463,18 @@ 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 .= + subs [("increment-and-print", + Fill $ \_ _ _ -> + do modify ((+1) :: Int -> Int) + s <- get + return (T.pack (show s)))] + "test1\ + \test2\ + \" + `shouldRenderM` "12" + +doctypeTests :: SpecWith (LarcenyHspecState ()) doctypeTests = do describe "doctypes" $ do it "should render blank doctypes" $ do @@ -483,7 +485,7 @@ doctypeTests = do "

Hello world

" `shouldRenderM` "

Hello world

" -conditionalTests :: SpecWith LarcenyHspecState +conditionalTests :: SpecWith (LarcenyHspecState ()) conditionalTests = do describe "conditionals" $ do let template cond = @@ -598,7 +600,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 @@ -613,7 +615,7 @@ fallbackTests = do 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 From 93ece9ca022d6febcc9aa90e5263ba4f0af59b57 Mon Sep 17 00:00:00 2001 From: Libby Horacek Date: Sun, 20 May 2018 16:42:58 -0400 Subject: [PATCH 02/14] Rename ProcessContext; add logger --- src/Web/Larceny/Internal.hs | 58 ++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/src/Web/Larceny/Internal.hs b/src/Web/Larceny/Internal.hs index f71cbe5..73b0a38 100644 --- a/src/Web/Larceny/Internal.hs +++ b/src/Web/Larceny/Internal.hs @@ -97,21 +97,21 @@ mk :: Overrides -> [Node] -> Template s mk o = f where f nodes = Template $ \pth m l -> - let pc = ProcessContext pth m l o f nodes in + let pc = LarcenyState pth m l o print f nodes in do s <- get T.concat <$> toUserState (pc s) (process nodes) -toProcessState :: StateT s IO a -> StateT (ProcessContext s) IO a +toProcessState :: StateT s IO a -> StateT (LarcenyState s) IO a toProcessState f = do pc <- get - (result, s') <- liftIO $ runStateT f (_pcState pc) + (result, s') <- liftIO $ runStateT f (_lAppState pc) pcState .= s' return result -toUserState :: ProcessContext s -> StateT (ProcessContext s) IO a -> StateT s IO a +toUserState :: LarcenyState s -> StateT (LarcenyState s) IO a -> StateT s IO a toUserState pc f = do s <- get - liftIO $ evalStateT f (pc { _pcState = s }) + liftIO $ evalStateT f (pc { _lAppState = s }) fillIn :: Blank -> Substitutions s -> Fill s fillIn tn m = fromMaybe (fallbackFill tn m) (M.lookup tn m) @@ -124,29 +124,33 @@ fallbackFill (Blank tn) m = 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 } +data LarcenyState s = + LarcenyState { _lPath :: [Text] + , _lSubs :: Substitutions s + , _lLib :: Library s + , _lOverrides :: Overrides + , _lLogger :: (Text -> IO ()) + , _lMk :: [Node] -> Template s + , _lNodes :: [Node] + , _lAppState :: 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 }) +pcSubs :: Lens' (LarcenyState s) (Substitutions s) +pcSubs = lens _lSubs (\pc s -> pc { _lSubs = s }) -pcNodes :: Lens' (ProcessContext s) [Node] -pcNodes = lens _pcNodes (\pc n -> pc { _pcNodes = n }) +pcNodes :: Lens' (LarcenyState s) [Node] +pcNodes = lens _lNodes (\pc n -> pc { _lNodes = n }) -pcState :: Lens' (ProcessContext s) s -pcState = lens _pcState (\pc s -> pc { _pcState = s }) +pcState :: Lens' (LarcenyState s) s +pcState = lens _lAppState (\pc s -> pc { _lAppState = s }) -type ProcessT s = StateT (ProcessContext s) IO [Text] +--type ProcessT s = StateT (LarcenyState s) IO [Text] + +type ProcessT s = StateT (LarcenyState s) IO [Text] add :: Substitutions s -> Template s -> Template s add mouter tpl = @@ -187,7 +191,7 @@ 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 +210,7 @@ tagToText overrides (Name mPf name) atrs processed = ++ processed ++ [" prefix <> name <> ">"] -attrsToText :: Attributes -> StateT (ProcessContext s) IO Text +attrsToText :: Attributes -> StateT (LarcenyState s) IO Text attrsToText attrs = T.concat <$> mapM attrToText (M.toList attrs) where attrToText (k,v) = do @@ -217,7 +221,7 @@ attrsToText attrs = toText (k, "") = " " <> k toText (k, v) = " " <> k <> "=\"" <> T.strip v <> "\"" -fillAttrs :: Attributes -> StateT (ProcessContext s) IO Attributes +fillAttrs :: Attributes -> StateT (LarcenyState s) IO Attributes fillAttrs attrs = M.fromList <$> mapM fill (M.toList attrs) where fill p = do let (unboundKeys, unboundValues) = eUnboundAttrs p @@ -225,9 +229,9 @@ 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 -> StateT (LarcenyState s) IO Text fillAttr eBlankText = - do (ProcessContext pth m l _ mko _ _) <- get + do (LarcenyState pth m l _ _ mko _ _) <- get toProcessState $ case eBlankText of Right hole -> unFill (fillIn hole m) mempty (pth, mko []) l @@ -241,7 +245,7 @@ processBlank :: Text -> [Node] -> ProcessT s processBlank tagName atr kids = do - (ProcessContext pth m l _ mko _ _) <- get + (LarcenyState pth m l _ _ mko _ _) <- get filled <- fillAttrs atr sequence [ toProcessState $ unFill (fillIn (Blank tagName) m) filled @@ -251,7 +255,7 @@ processBind :: Attributes -> [Node] -> ProcessT s processBind atr kids = do - (ProcessContext pth m l _ mko nodes _) <- get + (LarcenyState pth m l _ _ mko nodes _) <- get let tagName = atr M.! "tag" newSubs = subs [(tagName, Fill $ \_a _t _l -> runTemplate (mko kids) pth m l)] @@ -266,7 +270,7 @@ processApply :: Attributes -> [Node] -> ProcessT s processApply atr kids = do - (ProcessContext pth m l _ mko _ _) <- get + (LarcenyState pth m l _ _ mko _ _) <- get filledAttrs <- fillAttrs atr let (absolutePath, tplToApply) = findTemplateFromAttrs pth l filledAttrs contentTpl <- toProcessState $ runTemplate (mko kids) pth m l From 81303ea6d6327ba4ce134b226f916fd60149eeca Mon Sep 17 00:00:00 2001 From: Libby Horacek Date: Sun, 20 May 2018 16:52:54 -0400 Subject: [PATCH 03/14] Move LarcenyState to Types --- src/Web/Larceny/Internal.hs | 30 ++++++------------------------ src/Web/Larceny/Types.hs | 25 ++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/Web/Larceny/Internal.hs b/src/Web/Larceny/Internal.hs index 73b0a38..eee4d74 100644 --- a/src/Web/Larceny/Internal.hs +++ b/src/Web/Larceny/Internal.hs @@ -23,6 +23,12 @@ import Web.Larceny.Fills import Web.Larceny.Html (html5Nodes, html5SelfClosingNodes) import Web.Larceny.Svg (svgNodes) +-- | 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. + -- | Turn lazy text into templates. parse :: LT.Text -> Template s parse = parseWithOverrides defaultOverrides @@ -34,20 +40,6 @@ parseWithOverrides o t = (X.Document _ (X.Element _ _ nodes) _) = D.parseLT ("
" <> textWithoutDoctype <> "
") in mk o $! map (toLarcenyNode o) nodes --- | 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 - -data Element = PlainElement Name Attributes [Node] - | ApplyElement Attributes [Node] - | BindElement Attributes [Node] - | BlankElement Name Attributes [Node] - | DoctypeElement toLarcenyName :: X.Name -> Name toLarcenyName (X.Name tn _ _) = @@ -124,16 +116,6 @@ fallbackFill (Blank tn) m = do liftIO $ putStrLn ("Larceny: Missing fill for blank " <> show tn <> " in template " <> show pth) unFill fallback attr (pth, tpl) lib -data LarcenyState s = - LarcenyState { _lPath :: [Text] - , _lSubs :: Substitutions s - , _lLib :: Library s - , _lOverrides :: Overrides - , _lLogger :: (Text -> IO ()) - , _lMk :: [Node] -> Template s - , _lNodes :: [Node] - , _lAppState :: s } - infix 4 .= (.=) :: MonadState s m => ASetter s s a b -> b -> m () l .= b = modify (l .~ b) diff --git a/src/Web/Larceny/Types.hs b/src/Web/Larceny/Types.hs index d9ed1b0..0d06ab8 100644 --- a/src/Web/Larceny/Types.hs +++ b/src/Web/Larceny/Types.hs @@ -14,7 +14,10 @@ module Web.Larceny.Types ( Blank(..) , defaultOverrides , FromAttribute(..) , AttrError(..) - , ApplyError(..)) where + , ApplyError(..) + , LarcenyState(..) + , Node(..) + , Element(..)) where import Control.Exception import Control.Monad.State (StateT) @@ -26,6 +29,26 @@ import Data.Text (Text) import qualified Data.Text as T import Text.Read (readMaybe) +data Node = NodeElement Element + | NodeContent Text + | NodeComment Text + +data Element = PlainElement Name Attributes [Node] + | ApplyElement Attributes [Node] + | BindElement Attributes [Node] + | BlankElement Name Attributes [Node] + | DoctypeElement + +data LarcenyState s = + LarcenyState { _lPath :: [Text] + , _lSubs :: Substitutions s + , _lLib :: Library s + , _lOverrides :: Overrides + , _lLogger :: (Text -> IO ()) + , _lMk :: [Node] -> Template s + , _lNodes :: [Node] + , _lAppState :: s } + -- | 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. From 76769131be0694d1e8e57d1eed18394659beb567 Mon Sep 17 00:00:00 2001 From: Libby Horacek Date: Sun, 20 May 2018 19:32:41 -0400 Subject: [PATCH 04/14] Remove mk from LarcenyState The mk field isn't necessary because it's always just the mk function. --- src/Web/Larceny/Internal.hs | 18 +++++++++--------- src/Web/Larceny/Types.hs | 1 - 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Web/Larceny/Internal.hs b/src/Web/Larceny/Internal.hs index eee4d74..768fbd9 100644 --- a/src/Web/Larceny/Internal.hs +++ b/src/Web/Larceny/Internal.hs @@ -89,7 +89,7 @@ mk :: Overrides -> [Node] -> Template s mk o = f where f nodes = Template $ \pth m l -> - let pc = LarcenyState pth m l o print f nodes in + let pc = LarcenyState pth m l o print nodes in do s <- get T.concat <$> toUserState (pc s) (process nodes) @@ -213,10 +213,10 @@ fillAttrs attrs = M.fromList <$> mapM fill (M.toList attrs) fillAttr :: Either Text Blank -> StateT (LarcenyState s) IO Text fillAttr eBlankText = - do (LarcenyState pth m l _ _ mko _ _) <- get + do (LarcenyState pth m l o _ _ _) <- get toProcessState $ case eBlankText of - Right hole -> unFill (fillIn hole m) mempty (pth, mko []) l + Right hole -> unFill (fillIn hole m) mempty (pth, mk o []) l Left text -> return text -- Look up the Fill for the hole. Apply the Fill to a map of @@ -227,20 +227,20 @@ processBlank :: Text -> [Node] -> ProcessT s processBlank tagName atr kids = do - (LarcenyState pth m l _ _ mko _ _) <- get + (LarcenyState pth m l o _ _ _) <- get filled <- fillAttrs atr sequence [ toProcessState $ unFill (fillIn (Blank tagName) m) filled - (pth, add m (mko kids)) l] + (pth, add m (mk o kids)) l] processBind :: Attributes -> [Node] -> ProcessT s processBind atr kids = do - (LarcenyState pth m l _ _ mko nodes _) <- get + (LarcenyState pth m l o _ nodes _) <- get let tagName = atr M.! "tag" newSubs = subs [(tagName, Fill $ \_a _t _l -> - runTemplate (mko kids) pth m l)] + runTemplate (mk o kids) pth m l)] pcSubs .= newSubs `M.union` m process nodes @@ -252,10 +252,10 @@ processApply :: Attributes -> [Node] -> ProcessT s processApply atr kids = do - (LarcenyState pth m l _ _ mko _ _) <- get + (LarcenyState pth m l o _ _ _) <- get filledAttrs <- fillAttrs atr let (absolutePath, tplToApply) = findTemplateFromAttrs pth l filledAttrs - contentTpl <- toProcessState $ runTemplate (mko kids) pth m l + contentTpl <- toProcessState $ runTemplate (mk o kids) pth m l let contentSub = subs [("apply-content", rawTextFill contentTpl)] sequence [ toProcessState $ runTemplate tplToApply absolutePath (contentSub `M.union` m) l ] diff --git a/src/Web/Larceny/Types.hs b/src/Web/Larceny/Types.hs index 0d06ab8..19c7f6f 100644 --- a/src/Web/Larceny/Types.hs +++ b/src/Web/Larceny/Types.hs @@ -45,7 +45,6 @@ data LarcenyState s = , _lLib :: Library s , _lOverrides :: Overrides , _lLogger :: (Text -> IO ()) - , _lMk :: [Node] -> Template s , _lNodes :: [Node] , _lAppState :: s } From 5aaa4468851825524474cbdf6b47e698118116f2 Mon Sep 17 00:00:00 2001 From: Libby Horacek Date: Sun, 20 May 2018 19:41:29 -0400 Subject: [PATCH 05/14] Move nodes to an argument to the process function --- src/Web/Larceny/Internal.hs | 24 +++++++++--------------- src/Web/Larceny/Types.hs | 1 - 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/Web/Larceny/Internal.hs b/src/Web/Larceny/Internal.hs index 768fbd9..155a7f0 100644 --- a/src/Web/Larceny/Internal.hs +++ b/src/Web/Larceny/Internal.hs @@ -89,7 +89,7 @@ mk :: Overrides -> [Node] -> Template s mk o = f where f nodes = Template $ \pth m l -> - let pc = LarcenyState pth m l o print nodes in + let pc = LarcenyState pth m l o print in do s <- get T.concat <$> toUserState (pc s) (process nodes) @@ -124,14 +124,9 @@ l .= b = modify (l .~ b) pcSubs :: Lens' (LarcenyState s) (Substitutions s) pcSubs = lens _lSubs (\pc s -> pc { _lSubs = s }) -pcNodes :: Lens' (LarcenyState s) [Node] -pcNodes = lens _lNodes (\pc n -> pc { _lNodes = n }) - pcState :: Lens' (LarcenyState s) s pcState = lens _lAppState (\pc s -> pc { _lAppState = s }) ---type ProcessT s = StateT (LarcenyState s) IO [Text] - type ProcessT s = StateT (LarcenyState s) IO [Text] add :: Substitutions s -> Template s -> Template s @@ -141,10 +136,8 @@ add mouter tpl = process :: [Node] -> ProcessT s 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 [""] @@ -213,7 +206,7 @@ fillAttrs attrs = M.fromList <$> mapM fill (M.toList attrs) fillAttr :: Either Text Blank -> StateT (LarcenyState s) IO Text fillAttr eBlankText = - do (LarcenyState pth m l o _ _ _) <- get + do (LarcenyState pth m l o _ _) <- get toProcessState $ case eBlankText of Right hole -> unFill (fillIn hole m) mempty (pth, mk o []) l @@ -227,22 +220,23 @@ processBlank :: Text -> [Node] -> ProcessT s processBlank tagName atr kids = do - (LarcenyState pth m l o _ _ _) <- get + (LarcenyState pth m l o _ _) <- get filled <- fillAttrs atr sequence [ toProcessState $ unFill (fillIn (Blank tagName) m) filled (pth, add m (mk o kids)) l] processBind :: Attributes -> + [Node] -> [Node] -> ProcessT s -processBind atr kids = do - (LarcenyState pth m l o _ nodes _) <- get +processBind atr kids nextNodes = do + (LarcenyState pth m l o _ _) <- get let tagName = atr M.! "tag" newSubs = subs [(tagName, Fill $ \_a _t _l -> runTemplate (mk o kids) pth m l)] pcSubs .= newSubs `M.union` m - process nodes + 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 @@ -252,7 +246,7 @@ processApply :: Attributes -> [Node] -> ProcessT s processApply atr kids = do - (LarcenyState pth m l o _ _ _) <- get + (LarcenyState pth m l o _ _) <- get filledAttrs <- fillAttrs atr let (absolutePath, tplToApply) = findTemplateFromAttrs pth l filledAttrs contentTpl <- toProcessState $ runTemplate (mk o kids) pth m l diff --git a/src/Web/Larceny/Types.hs b/src/Web/Larceny/Types.hs index 19c7f6f..1f74c1d 100644 --- a/src/Web/Larceny/Types.hs +++ b/src/Web/Larceny/Types.hs @@ -45,7 +45,6 @@ data LarcenyState s = , _lLib :: Library s , _lOverrides :: Overrides , _lLogger :: (Text -> IO ()) - , _lNodes :: [Node] , _lAppState :: s } -- | Corresponds to a "blank" in the template that can be filled in From 560aeb8dd9c2ed079cb8da52a24f61aec49d2474 Mon Sep 17 00:00:00 2001 From: Libby Horacek Date: Mon, 21 May 2018 09:07:34 -0400 Subject: [PATCH 06/14] Rename ProcessT to LarcenyM --- src/Web/Larceny/Internal.hs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Web/Larceny/Internal.hs b/src/Web/Larceny/Internal.hs index 155a7f0..5edfcec 100644 --- a/src/Web/Larceny/Internal.hs +++ b/src/Web/Larceny/Internal.hs @@ -93,14 +93,14 @@ mk o = f do s <- get T.concat <$> toUserState (pc s) (process nodes) -toProcessState :: StateT s IO a -> StateT (LarcenyState s) IO a +toProcessState :: StateT s IO a -> LarcenyM s a toProcessState f = do pc <- get (result, s') <- liftIO $ runStateT f (_lAppState pc) pcState .= s' return result -toUserState :: LarcenyState s -> StateT (LarcenyState s) IO a -> StateT s IO a +toUserState :: LarcenyState s -> LarcenyM s a -> StateT s IO a toUserState pc f = do s <- get liftIO $ evalStateT f (pc { _lAppState = s }) @@ -127,13 +127,13 @@ pcSubs = lens _lSubs (\pc s -> pc { _lSubs = s }) pcState :: Lens' (LarcenyState s) s pcState = lens _lAppState (\pc s -> pc { _lAppState = s }) -type ProcessT s = StateT (LarcenyState s) IO [Text] +type LarcenyM s a = StateT (LarcenyState s) IO a add :: Substitutions s -> Template s -> Template s add mouter tpl = Template (\pth minner l -> runTemplate tpl pth (minner `M.union` mouter) l) -process :: [Node] -> ProcessT s +process :: [Node] -> LarcenyM s [Text] process [] = return [] process (NodeElement (BindElement atr kids):nextNodes) = do processBind atr kids nextNodes @@ -161,7 +161,7 @@ process (currentNode:nextNodes) = do processPlain :: Name -> Attributes -> [Node] -> - ProcessT s + LarcenyM s [Text] processPlain tagName atr kids = do pc <- get atrs <- attrsToText atr @@ -185,7 +185,7 @@ tagToText overrides (Name mPf name) atrs processed = ++ processed ++ [" prefix <> name <> ">"] -attrsToText :: Attributes -> StateT (LarcenyState s) IO Text +attrsToText :: Attributes -> LarcenyM s Text attrsToText attrs = T.concat <$> mapM attrToText (M.toList attrs) where attrToText (k,v) = do @@ -196,7 +196,7 @@ attrsToText attrs = toText (k, "") = " " <> k toText (k, v) = " " <> k <> "=\"" <> T.strip v <> "\"" -fillAttrs :: Attributes -> StateT (LarcenyState 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 @@ -204,7 +204,7 @@ fillAttrs attrs = M.fromList <$> mapM fill (M.toList attrs) vals <- T.concat <$> mapM fillAttr unboundValues return (keys, vals) -fillAttr :: Either Text Blank -> StateT (LarcenyState s) IO Text +fillAttr :: Either Text Blank -> LarcenyM s Text fillAttr eBlankText = do (LarcenyState pth m l o _ _) <- get toProcessState $ @@ -218,7 +218,7 @@ fillAttr eBlankText = processBlank :: Text -> Attributes -> [Node] -> - ProcessT s + LarcenyM s [Text] processBlank tagName atr kids = do (LarcenyState pth m l o _ _) <- get filled <- fillAttrs atr @@ -229,7 +229,7 @@ processBlank tagName atr kids = do processBind :: Attributes -> [Node] -> [Node] -> - ProcessT s + LarcenyM s [Text] processBind atr kids nextNodes = do (LarcenyState pth m l o _ _) <- get let tagName = atr M.! "tag" @@ -244,7 +244,7 @@ processBind atr kids nextNodes = do -- combined with outer substitution and the library. processApply :: Attributes -> [Node] -> - ProcessT s + LarcenyM s [Text] processApply atr kids = do (LarcenyState pth m l o _ _) <- get filledAttrs <- fillAttrs atr From 749aa3037f4e18bc866cd9a330c47ca392fb7168 Mon Sep 17 00:00:00 2001 From: Libby Horacek Date: Mon, 21 May 2018 09:12:18 -0400 Subject: [PATCH 07/14] Move Node and Elements back to Internal No longer used by LarcenyState Move LarcenyM to Types --- src/Web/Larceny/Internal.hs | 12 ++++++++++-- src/Web/Larceny/Types.hs | 13 ++----------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/Web/Larceny/Internal.hs b/src/Web/Larceny/Internal.hs index 5edfcec..5254734 100644 --- a/src/Web/Larceny/Internal.hs +++ b/src/Web/Larceny/Internal.hs @@ -29,6 +29,16 @@ import Web.Larceny.Svg (svgNodes) -- 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 + +data Element = PlainElement Name Attributes [Node] + | ApplyElement Attributes [Node] + | BindElement Attributes [Node] + | BlankElement Name Attributes [Node] + | DoctypeElement + -- | Turn lazy text into templates. parse :: LT.Text -> Template s parse = parseWithOverrides defaultOverrides @@ -127,8 +137,6 @@ pcSubs = lens _lSubs (\pc s -> pc { _lSubs = s }) pcState :: Lens' (LarcenyState s) s pcState = lens _lAppState (\pc s -> pc { _lAppState = s }) -type LarcenyM s a = StateT (LarcenyState s) IO a - add :: Substitutions s -> Template s -> Template s add mouter tpl = Template (\pth minner l -> runTemplate tpl pth (minner `M.union` mouter) l) diff --git a/src/Web/Larceny/Types.hs b/src/Web/Larceny/Types.hs index 1f74c1d..25ab7d5 100644 --- a/src/Web/Larceny/Types.hs +++ b/src/Web/Larceny/Types.hs @@ -16,8 +16,7 @@ module Web.Larceny.Types ( Blank(..) , AttrError(..) , ApplyError(..) , LarcenyState(..) - , Node(..) - , Element(..)) where + , LarcenyM) where import Control.Exception import Control.Monad.State (StateT) @@ -29,15 +28,7 @@ import Data.Text (Text) import qualified Data.Text as T import Text.Read (readMaybe) -data Node = NodeElement Element - | NodeContent Text - | NodeComment Text - -data Element = PlainElement Name Attributes [Node] - | ApplyElement Attributes [Node] - | BindElement Attributes [Node] - | BlankElement Name Attributes [Node] - | DoctypeElement +type LarcenyM s a = StateT (LarcenyState s) IO a data LarcenyState s = LarcenyState { _lPath :: [Text] From e9919f1d73f68e6e1d03ed7545f2c122162e8315 Mon Sep 17 00:00:00 2001 From: Libby Horacek Date: Mon, 21 May 2018 21:09:37 -0400 Subject: [PATCH 08/14] Change types of Fill and Template to use LarcenyM --- src/Web/Larceny.hs | 7 +++-- src/Web/Larceny/Fills.hs | 8 ++--- src/Web/Larceny/Internal.hs | 63 ++++++++++++------------------------- src/Web/Larceny/Types.hs | 38 +++++++++++++++++++--- test/Examples.hs | 3 +- test/Spec.hs | 43 +++++++++++-------------- 6 files changed, 84 insertions(+), 78 deletions(-) diff --git a/src/Web/Larceny.hs b/src/Web/Larceny.hs index 9742f4d..3597530 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 pth sub l) 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..f0e1ca7 100644 --- a/src/Web/Larceny/Fills.hs +++ b/src/Web/Larceny/Fills.hs @@ -100,7 +100,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 $ \_m _t _l -> 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 $ \_m _t _l -> toLarcenyState t -- | Create substitutions for each element in a list and fill the child nodes -- with those substitutions. @@ -133,7 +133,7 @@ mapSubs' :: (a -> StateT s IO (Substitutions s)) -> [a] -> Fill s mapSubs' f xs = Fill $ \_m (pth, tpl) lib -> T.concat <$> mapM (\x -> do - s' <- f x + s' <- toLarcenyState $ f x runTemplate tpl pth s' lib) xs -- | Fill in the child nodes of the blank with substitutions already @@ -199,7 +199,7 @@ 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 + mSubs <- toLarcenyState sMSubs case mSubs of Nothing -> return "" Just s -> tpl pth s l diff --git a/src/Web/Larceny/Internal.hs b/src/Web/Larceny/Internal.hs index 5254734..8e1cb11 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 Control.Monad.State (MonadState, StateT, evalStateT, get, + modify, put, runStateT) 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) @@ -15,13 +15,14 @@ import Data.Monoid ((<>)) import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Lazy as LT +import Lens.Micro 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) +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, @@ -48,7 +49,7 @@ 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 + in mk $! map (toLarcenyNode o) nodes toLarcenyName :: X.Name -> Name @@ -95,25 +96,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 = LarcenyState pth m l o print in - do s <- get - T.concat <$> toUserState (pc s) (process nodes) - -toProcessState :: StateT s IO a -> LarcenyM s a -toProcessState f = - do pc <- get - (result, s') <- liftIO $ runStateT f (_lAppState pc) - pcState .= s' - return result - -toUserState :: LarcenyState s -> LarcenyM s a -> StateT s IO a -toUserState pc f = - do s <- get - liftIO $ evalStateT f (pc { _lAppState = s }) + Template $ \pth m _l -> + do 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) @@ -126,17 +115,6 @@ fallbackFill (Blank tn) m = do liftIO $ putStrLn ("Larceny: Missing fill for blank " <> show tn <> " in template " <> show pth) unFill fallback attr (pth, tpl) lib -infix 4 .= -(.=) :: MonadState s m => ASetter s s a b -> b -> m () -l .= b = modify (l .~ b) -{-# INLINE (.=) #-} - -pcSubs :: Lens' (LarcenyState s) (Substitutions s) -pcSubs = lens _lSubs (\pc s -> pc { _lSubs = s }) - -pcState :: Lens' (LarcenyState s) s -pcState = lens _lAppState (\pc s -> pc { _lAppState = s }) - add :: Substitutions s -> Template s -> Template s add mouter tpl = Template (\pth minner l -> runTemplate tpl pth (minner `M.union` mouter) l) @@ -215,9 +193,8 @@ fillAttrs attrs = M.fromList <$> mapM fill (M.toList attrs) fillAttr :: Either Text Blank -> LarcenyM s Text fillAttr eBlankText = do (LarcenyState pth m l o _ _) <- get - toProcessState $ - case eBlankText of - Right hole -> unFill (fillIn hole m) mempty (pth, mk o []) l + case eBlankText of + Right hole -> unFill (fillIn hole m) mempty (pth, mk []) l Left text -> return text -- Look up the Fill for the hole. Apply the Fill to a map of @@ -230,9 +207,9 @@ processBlank :: Text -> processBlank tagName atr kids = do (LarcenyState pth m l o _ _) <- get filled <- fillAttrs atr - sequence [ toProcessState $ unFill (fillIn (Blank tagName) m) + sequence [ unFill (fillIn (Blank tagName) m) filled - (pth, add m (mk o kids)) l] + (pth, add m (mk kids)) l] processBind :: Attributes -> [Node] -> @@ -242,8 +219,8 @@ processBind atr kids nextNodes = do (LarcenyState pth m l o _ _) <- get let tagName = atr M.! "tag" newSubs = subs [(tagName, Fill $ \_a _t _l -> - runTemplate (mk o kids) pth m l)] - pcSubs .= newSubs `M.union` m + runTemplate (mk kids) pth m l)] + lSubs .= newSubs `M.union` m process nextNodes -- Look up the template that's supposed to be applied in the library, @@ -257,10 +234,10 @@ processApply atr kids = do (LarcenyState pth m l o _ _) <- get filledAttrs <- fillAttrs atr let (absolutePath, tplToApply) = findTemplateFromAttrs pth l filledAttrs - contentTpl <- toProcessState $ runTemplate (mk o kids) pth m l + contentTpl <- runTemplate (mk kids) pth m l let contentSub = subs [("apply-content", rawTextFill contentTpl)] - sequence [ toProcessState $ runTemplate tplToApply absolutePath (contentSub `M.union` m) l ] + sequence [ runTemplate tplToApply absolutePath (contentSub `M.union` m) l ] findTemplateFromAttrs :: Path -> Library s -> diff --git a/src/Web/Larceny/Types.hs b/src/Web/Larceny/Types.hs index 25ab7d5..ea196a8 100644 --- a/src/Web/Larceny/Types.hs +++ b/src/Web/Larceny/Types.hs @@ -16,10 +16,17 @@ module Web.Larceny.Types ( Blank(..) , AttrError(..) , ApplyError(..) , LarcenyState(..) - , LarcenyM) where + , LarcenyM + , toLarcenyState + , (.=) + , lState + , lSubs + , lPath) where import Control.Exception -import Control.Monad.State (StateT) +import Control.Monad.State (StateT, evalStateT, get, runStateT, modify, MonadState) +import Lens.Micro +import Control.Monad.Trans (liftIO) import Data.Hashable (Hashable, hash, hashWithSalt) import Data.Map (Map) import qualified Data.Map as M @@ -38,6 +45,29 @@ data LarcenyState s = , _lLogger :: (Text -> IO ()) , _lAppState :: s } +-- Temporary while transitioning to LarcenyM +infix 4 .= +(.=) :: MonadState s m => ASetter s s a b -> b -> m () +l .= b = modify (l .~ b) +{-# INLINE (.=) #-} + +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 }) + +lState :: Lens' (LarcenyState s) s +lState = lens _lAppState (\l s -> l { _lAppState = s }) + +toLarcenyState :: StateT s IO a -> LarcenyM s a +toLarcenyState f = + do l <- get + (result, s') <- liftIO $ runStateT f (_lAppState l) + lState .= s' + return result +-- End temporary + -- | 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. @@ -82,7 +112,7 @@ instance Hashable Blank where newtype Fill s = Fill { unFill :: Attributes -> (Path, Template s) -> Library s - -> StateT s IO Text } + -> LarcenyM s Text } -- | The Blank's attributes, a map from the attribute name to -- it's value. @@ -131,7 +161,7 @@ fallbackSub fill = M.fromList [(FallbackBlank, fill)] newtype Template s = Template { runTemplate :: Path -> Substitutions s -> Library s - -> StateT s IO Text } + -> LarcenyM s Text } -- | The path to a template. type Path = [Text] diff --git a/test/Examples.hs b/test/Examples.hs index 0b1c4e0..7318519 100644 --- a/test/Examples.hs +++ b/test/Examples.hs @@ -66,7 +66,8 @@ modifyInnerText :: (Text -> Text) -> Fill () modifyInnerText f = Fill $ \_attrs (_pth, tpl) _l -> liftIO $ do - t' <- evalStateT (runTemplate tpl ["default"] mempty mempty) () + let larcenyState = LarcenyState ["default"] mempty mempty defaultOverrides print () + t' <- evalStateT (runTemplate tpl ["default"] mempty mempty) larcenyState return $ f t' tplLib :: Library () diff --git a/test/Spec.hs b/test/Spec.hs index 88d00eb..4c4399e 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -32,14 +32,6 @@ infix 4 .= l .= b = modify (l .~ b) {-# INLINE (.=) #-} -data LarcenyState s = - LarcenyState { _lPath :: [Text] - , _lSubs :: Substitutions s - , _lLib :: Library s - , _lOverrides :: Overrides - , _lLogger :: (Text -> IO ()) - , _lAppState :: s } - lPath :: Lens' (LarcenyState s) [Text] lPath = lens _lPath (\ls p -> ls { _lPath = p }) lSubs :: Lens' (LarcenyState s) (Substitutions s) @@ -48,6 +40,8 @@ lLib :: Lens' (LarcenyState s) (Library s) lLib = lens _lLib (\ls l -> ls { _lLib = l }) 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 }) type LarcenyHspecM s = StateT (LarcenyHspecState s) IO @@ -116,9 +110,9 @@ removeSpaces = T.replace " " "" renderM :: Text -> LarcenyHspecM s Text renderM templateText = do - (LarcenyHspecState _ (LarcenyState p s l o _ st)) <- S.get + (LarcenyHspecState _ ls@(LarcenyState p s l o _ st)) <- S.get let tpl = parseWithOverrides o (LT.fromStrict templateText) - liftIO $ evalStateT (runTemplate tpl p s l) st + liftIO $ evalStateT (runTemplate tpl p s l) ls shouldRenderM :: Text -> Text -> LarcenyHspecM s () shouldRenderM templateText output = do @@ -446,14 +440,18 @@ namespaceTests = 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 - hLarcenyState.lSubs .= - subs [("increment-and-print", - Fill $ \_ _ _ -> - do modify ((+1) :: Int -> Int) - s <- get - return (T.pack (show s)))] + hLarcenyState.lSubs .= incrementSub "" `shouldRenderM` "12" {- The following test was prompted by a bug where I refuktored the @@ -463,12 +461,7 @@ statefulTests = over and over again. -} it "should not be affected by binds" $ do - hLarcenyState.lSubs .= - subs [("increment-and-print", - Fill $ \_ _ _ -> - do modify ((+1) :: Int -> Int) - s <- get - return (T.pack (show s)))] + hLarcenyState.lSubs .= incrementSub "test1\ \test2\ \" @@ -637,7 +630,8 @@ attrTests = let descTplFill = useAttrs (a"length") (\n -> Fill $ \_attrs (_pth, tpl) _l -> liftIO $ do - t' <- evalStateT (runTemplate tpl ["default"] mempty mempty) () + let larcenyState = LarcenyState ["default"] mempty mempty defaultOverrides print () + t' <- evalStateT (runTemplate tpl ["default"] mempty mempty) larcenyState return $ T.take n t' <> "...") hLarcenyState.lSubs .= subs [ ("adverb", textFill "really") , ("desc", descTplFill)] @@ -677,7 +671,8 @@ attrTests = descFunc n e = Fill $ do let ending = fromMaybe "..." e \_attrs (_pth, tpl) _l -> liftIO $ do - renderedText <- evalStateT (runTemplate tpl ["default"] mempty mempty) () + let larcenyState = LarcenyState ["default"] mempty mempty defaultOverrides print () + renderedText <- evalStateT (runTemplate tpl ["default"] mempty mempty) larcenyState return $ T.take n renderedText <> ending {-# ANN module ("HLint: ignore Redundant do" :: String) #-} From 723ff0e8ddd1f19d9a86974326a7a1ef81ac7daa Mon Sep 17 00:00:00 2001 From: Libby Horacek Date: Mon, 21 May 2018 21:26:57 -0400 Subject: [PATCH 09/14] Remove unnecessary params from Fill --- src/Web/Larceny/Fills.hs | 35 ++++++++++++++++++++++++----------- src/Web/Larceny/Internal.hs | 16 +++++++++------- src/Web/Larceny/Types.hs | 6 ++++-- test/Examples.hs | 2 +- test/Spec.hs | 21 ++++++++++++--------- 5 files changed, 50 insertions(+), 30 deletions(-) diff --git a/src/Web/Larceny/Fills.hs b/src/Web/Larceny/Fills.hs index f0e1ca7..782bcbb 100644 --- a/src/Web/Larceny/Fills.hs +++ b/src/Web/Larceny/Fills.hs @@ -17,7 +17,7 @@ module Web.Larceny.Fills ( textFill , (%)) where import Control.Exception -import Control.Monad.State (StateT) +import Control.Monad.State (StateT, get) import qualified Data.Map as M import Data.Maybe (fromMaybe) import Data.Text (Text) @@ -100,7 +100,7 @@ rawTextFill t = rawTextFill' (return t) -- textFill' getTextFromDatabase -- @ textFill' :: StateT s IO Text -> Fill s -textFill' t = Fill $ \_m _t _l -> HE.text <$> toLarcenyState 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 <$> toLarcenyState t -- textFill' getTextFromDatabase -- @ rawTextFill' :: StateT s IO Text -> Fill s -rawTextFill' t = Fill $ \_m _t _l -> toLarcenyState 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,14 +124,20 @@ rawTextFill' t = Fill $ \_m _t _l -> toLarcenyState t mapSubs :: (a -> Substitutions s) -> [a] -> Fill s -mapSubs f xs = Fill $ \_attrs (pth, tpl) lib -> +mapSubs f xs = Fill $ \_attrs tpl -> do + s <- get + let pth = _lPath s + let lib = _lLib s T.concat <$> mapM (\n -> runTemplate tpl pth (f n) lib) 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 + s <- get + let pth = _lPath s + let lib = _lLib s T.concat <$> mapM (\x -> do s' <- toLarcenyState $ f x runTemplate tpl pth s' lib) xs @@ -182,8 +188,11 @@ 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 + st <- get + let pth = _lPath st + let lib = _lLib st + runTemplate tpl pth s lib -- | Use state and IO and maybe fill in with some substitutions. -- @@ -198,11 +207,15 @@ 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 +maybeFillChildrenWith' sMSubs = Fill $ \_s (Template tpl) -> do mSubs <- toLarcenyState sMSubs case mSubs of Nothing -> return "" - Just s -> tpl pth s l + Just s -> do + st <- get + let pth = _lPath st + let lib = _lLib st + tpl pth s lib -- | Use attributes from the the blank as arguments to the fill. -- @@ -223,8 +236,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 8e1cb11..a13604e 100644 --- a/src/Web/Larceny/Internal.hs +++ b/src/Web/Larceny/Internal.hs @@ -111,9 +111,11 @@ 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 + 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 = @@ -194,7 +196,7 @@ fillAttr :: Either Text Blank -> LarcenyM s Text fillAttr eBlankText = do (LarcenyState pth m l o _ _) <- get case eBlankText of - Right hole -> unFill (fillIn hole m) mempty (pth, mk []) l + 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 @@ -209,7 +211,7 @@ processBlank tagName atr kids = do filled <- fillAttrs atr sequence [ unFill (fillIn (Blank tagName) m) filled - (pth, add m (mk kids)) l] + (add m (mk kids))] processBind :: Attributes -> [Node] -> @@ -218,8 +220,8 @@ processBind :: Attributes -> processBind atr kids nextNodes = do (LarcenyState pth m l o _ _) <- get let tagName = atr M.! "tag" - newSubs = subs [(tagName, Fill $ \_a _t _l -> - runTemplate (mk kids) pth m l)] + newSubs = subs [(tagName, Fill $ \_a _t ->do + runTemplate (mk kids) pth m l)] lSubs .= newSubs `M.union` m process nextNodes diff --git a/src/Web/Larceny/Types.hs b/src/Web/Larceny/Types.hs index ea196a8..9f3b418 100644 --- a/src/Web/Larceny/Types.hs +++ b/src/Web/Larceny/Types.hs @@ -57,6 +57,9 @@ 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 }) + lState :: Lens' (LarcenyState s) s lState = lens _lAppState (\l s -> l { _lAppState = s }) @@ -110,8 +113,7 @@ instance Hashable Blank where -- 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 + -> Template s -> LarcenyM s Text } -- | The Blank's attributes, a map from the attribute name to diff --git a/test/Examples.hs b/test/Examples.hs index 7318519..b5de327 100644 --- a/test/Examples.hs +++ b/test/Examples.hs @@ -64,7 +64,7 @@ subst = subs [ ("site-title", textFill "Gotham Girls roster") modifyInnerText :: (Text -> Text) -> Fill () modifyInnerText f = Fill $ - \_attrs (_pth, tpl) _l -> + \_attrs tpl -> liftIO $ do let larcenyState = LarcenyState ["default"] mempty mempty defaultOverrides print () t' <- evalStateT (runTemplate tpl ["default"] mempty mempty) larcenyState diff --git a/test/Spec.hs b/test/Spec.hs index 4c4399e..ef67911 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -287,7 +287,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 " \ @@ -315,7 +315,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' @@ -324,8 +324,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' @@ -373,7 +373,10 @@ 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` "

" @@ -442,7 +445,7 @@ statefulTests :: SpecWith (LarcenyHspecState Int) statefulTests = let incrementSub = subs [("increment-and-print", - Fill $ \_ _ _ -> + Fill $ \_ _ -> do -- eek refactor later s <- get lAppState .= (_lAppState s + 1 :: Int) @@ -605,7 +608,7 @@ 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 ()) @@ -629,7 +632,7 @@ attrTests = it "should allow you use child elements" $ do let descTplFill = useAttrs (a"length") - (\n -> Fill $ \_attrs (_pth, tpl) _l -> liftIO $ do + (\n -> Fill $ \_attrs tpl -> liftIO $ do let larcenyState = LarcenyState ["default"] mempty mempty defaultOverrides print () t' <- evalStateT (runTemplate tpl ["default"] mempty mempty) larcenyState return $ T.take n t' <> "...") @@ -670,7 +673,7 @@ attrTests = descFunc :: Int -> Maybe Text -> Fill () descFunc n e = Fill $ do let ending = fromMaybe "..." e - \_attrs (_pth, tpl) _l -> liftIO $ do + \_attrs tpl -> liftIO $ do let larcenyState = LarcenyState ["default"] mempty mempty defaultOverrides print () renderedText <- evalStateT (runTemplate tpl ["default"] mempty mempty) larcenyState return $ T.take n renderedText <> ending From b7743f313ba780749d886a348de134b48c47b838 Mon Sep 17 00:00:00 2001 From: Libby Horacek Date: Mon, 21 May 2018 21:36:44 -0400 Subject: [PATCH 10/14] Remove unnecessary params from Template --- src/Web/Larceny.hs | 2 +- src/Web/Larceny/Fills.hs | 21 ++++----------------- src/Web/Larceny/Internal.hs | 13 +++++++------ src/Web/Larceny/Types.hs | 4 +--- test/Examples.hs | 2 +- test/Spec.hs | 6 +++--- 6 files changed, 17 insertions(+), 31 deletions(-) diff --git a/src/Web/Larceny.hs b/src/Web/Larceny.hs index 3597530..1ea9b6d 100644 --- a/src/Web/Larceny.hs +++ b/src/Web/Larceny.hs @@ -133,7 +133,7 @@ renderRelative l sub s givenPath targetPath = case findTemplate l givenPath targetPath of (pth, Just (Template run)) -> let larcenyState = LarcenyState pth sub l defaultOverrides print s in - Just <$> evalStateT (run pth sub l) larcenyState + 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 782bcbb..8bbf8a9 100644 --- a/src/Web/Larceny/Fills.hs +++ b/src/Web/Larceny/Fills.hs @@ -125,22 +125,16 @@ mapSubs :: (a -> Substitutions s) -> [a] -> Fill s mapSubs f xs = Fill $ \_attrs tpl -> do - s <- get - let pth = _lPath s - let lib = _lLib s - T.concat <$> mapM (\n -> runTemplate tpl pth (f n) lib) xs + 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 tpl -> do - s <- get - let pth = _lPath s - let lib = _lLib s T.concat <$> mapM (\x -> do s' <- toLarcenyState $ f x - runTemplate tpl pth s' lib) xs + runTemplate tpl s') xs -- | Fill in the child nodes of the blank with substitutions already -- available. @@ -189,10 +183,7 @@ fillChildrenWith' m = maybeFillChildrenWith' (Just <$> m) maybeFillChildrenWith :: Maybe (Substitutions s) -> Fill s maybeFillChildrenWith Nothing = textFill "" maybeFillChildrenWith (Just s) = Fill $ \_attrs tpl -> do - st <- get - let pth = _lPath st - let lib = _lLib st - runTemplate tpl pth s lib + runTemplate tpl s -- | Use state and IO and maybe fill in with some substitutions. -- @@ -211,11 +202,7 @@ maybeFillChildrenWith' sMSubs = Fill $ \_s (Template tpl) -> do mSubs <- toLarcenyState sMSubs case mSubs of Nothing -> return "" - Just s -> do - st <- get - let pth = _lPath st - let lib = _lLib st - tpl pth s lib + Just s -> tpl s -- | Use attributes from the the blank as arguments to the fill. -- diff --git a/src/Web/Larceny/Internal.hs b/src/Web/Larceny/Internal.hs index a13604e..d479504 100644 --- a/src/Web/Larceny/Internal.hs +++ b/src/Web/Larceny/Internal.hs @@ -99,9 +99,9 @@ toLarcenyNode _ (X.NodeInstruction _) = NodeContent "" mk :: [Node] -> Template s mk = f where f nodes = - Template $ \pth m _l -> + Template $ \m -> do lSubs .= m - lPath .= pth + -- lPath .= pth T.concat <$> process nodes fillIn :: Blank -> Substitutions s -> Fill s @@ -119,7 +119,7 @@ fallbackFill (Blank tn) m = 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] -> LarcenyM s [Text] process [] = return [] @@ -221,7 +221,7 @@ processBind atr kids nextNodes = do (LarcenyState pth m l o _ _) <- get let tagName = atr M.! "tag" newSubs = subs [(tagName, Fill $ \_a _t ->do - runTemplate (mk kids) pth m l)] + runTemplate (mk kids) m)] lSubs .= newSubs `M.union` m process nextNodes @@ -236,10 +236,11 @@ processApply atr kids = do (LarcenyState pth m l o _ _) <- get filledAttrs <- fillAttrs atr let (absolutePath, tplToApply) = findTemplateFromAttrs pth l filledAttrs - contentTpl <- runTemplate (mk kids) pth m l + contentTpl <- runTemplate (mk kids) m let contentSub = subs [("apply-content", rawTextFill contentTpl)] - sequence [ runTemplate tplToApply absolutePath (contentSub `M.union` m) l ] + lPath .= absolutePath + sequence [ runTemplate tplToApply (contentSub `M.union` m) ] findTemplateFromAttrs :: Path -> Library s -> diff --git a/src/Web/Larceny/Types.hs b/src/Web/Larceny/Types.hs index 9f3b418..a2f64d8 100644 --- a/src/Web/Larceny/Types.hs +++ b/src/Web/Larceny/Types.hs @@ -160,9 +160,7 @@ 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 +newtype Template s = Template { runTemplate :: Substitutions s -> LarcenyM s Text } -- | The path to a template. diff --git a/test/Examples.hs b/test/Examples.hs index b5de327..2dca620 100644 --- a/test/Examples.hs +++ b/test/Examples.hs @@ -67,7 +67,7 @@ modifyInnerText f = Fill $ \_attrs tpl -> liftIO $ do let larcenyState = LarcenyState ["default"] mempty mempty defaultOverrides print () - t' <- evalStateT (runTemplate tpl ["default"] mempty mempty) larcenyState + t' <- evalStateT (runTemplate tpl mempty) larcenyState return $ f t' tplLib :: Library () diff --git a/test/Spec.hs b/test/Spec.hs index ef67911..b643dda 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -112,7 +112,7 @@ renderM :: Text -> LarcenyHspecM s Text renderM templateText = do (LarcenyHspecState _ ls@(LarcenyState p s l o _ st)) <- S.get let tpl = parseWithOverrides o (LT.fromStrict templateText) - liftIO $ evalStateT (runTemplate tpl p s l) ls + liftIO $ evalStateT (runTemplate tpl s) ls shouldRenderM :: Text -> Text -> LarcenyHspecM s () shouldRenderM templateText output = do @@ -634,7 +634,7 @@ attrTests = useAttrs (a"length") (\n -> Fill $ \_attrs tpl -> liftIO $ do let larcenyState = LarcenyState ["default"] mempty mempty defaultOverrides print () - t' <- evalStateT (runTemplate tpl ["default"] mempty mempty) larcenyState + t' <- evalStateT (runTemplate tpl mempty) larcenyState return $ T.take n t' <> "...") hLarcenyState.lSubs .= subs [ ("adverb", textFill "really") , ("desc", descTplFill)] @@ -675,7 +675,7 @@ attrTests = do let ending = fromMaybe "..." e \_attrs tpl -> liftIO $ do let larcenyState = LarcenyState ["default"] mempty mempty defaultOverrides print () - renderedText <- evalStateT (runTemplate tpl ["default"] mempty mempty) larcenyState + renderedText <- evalStateT (runTemplate tpl mempty) larcenyState return $ T.take n renderedText <> ending {-# ANN module ("HLint: ignore Redundant do" :: String) #-} From 2ab71fd40e0a8a1ce05bb20af6794ae7313e467a Mon Sep 17 00:00:00 2001 From: Libby Horacek Date: Tue, 22 May 2018 10:27:16 -0400 Subject: [PATCH 11/14] Add Legacy module to keep same API Ideally, the legacy module will allow apps to continue using existing Fills just by importing Web.Larceny.Legacy instead of Web.Larceny.Fills. --- larceny.cabal | 1 + src/Web/Larceny/Legacy.hs | 92 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 src/Web/Larceny/Legacy.hs 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/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 From e308389bec4da1479e8dece9e1246ad1bddefd59 Mon Sep 17 00:00:00 2001 From: Libby Horacek Date: Wed, 23 May 2018 10:28:15 -0400 Subject: [PATCH 12/14] Fix warnings --- src/Web/Larceny/Fills.hs | 2 +- src/Web/Larceny/Internal.hs | 12 +++++------- src/Web/Larceny/Types.hs | 5 +++-- test/Spec.hs | 2 +- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/Web/Larceny/Fills.hs b/src/Web/Larceny/Fills.hs index 8bbf8a9..2033d23 100644 --- a/src/Web/Larceny/Fills.hs +++ b/src/Web/Larceny/Fills.hs @@ -17,7 +17,7 @@ module Web.Larceny.Fills ( textFill , (%)) where import Control.Exception -import Control.Monad.State (StateT, get) +import Control.Monad.State (StateT) import qualified Data.Map as M import Data.Maybe (fromMaybe) import Data.Text (Text) diff --git a/src/Web/Larceny/Internal.hs b/src/Web/Larceny/Internal.hs index d479504..e452805 100644 --- a/src/Web/Larceny/Internal.hs +++ b/src/Web/Larceny/Internal.hs @@ -5,8 +5,7 @@ module Web.Larceny.Internal ( findTemplate , parseWithOverrides) where import Control.Exception -import Control.Monad.State (MonadState, StateT, evalStateT, get, - modify, put, runStateT) +import Control.Monad.State (get) import Control.Monad.Trans (liftIO) import qualified Data.HashSet as HS import qualified Data.Map as M @@ -15,7 +14,6 @@ import Data.Monoid ((<>)) import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Lazy as LT -import Lens.Micro import qualified Text.HTML.DOM as D import qualified Text.XML as X ------------ @@ -194,7 +192,7 @@ fillAttrs attrs = M.fromList <$> mapM fill (M.toList attrs) fillAttr :: Either Text Blank -> LarcenyM s Text fillAttr eBlankText = - do (LarcenyState pth m l o _ _) <- get + do m <- _lSubs <$> get case eBlankText of Right hole -> unFill (fillIn hole m) mempty (mk []) Left text -> return text @@ -207,7 +205,7 @@ processBlank :: Text -> [Node] -> LarcenyM s [Text] processBlank tagName atr kids = do - (LarcenyState pth m l o _ _) <- get + m <- _lSubs <$> get filled <- fillAttrs atr sequence [ unFill (fillIn (Blank tagName) m) filled @@ -218,7 +216,7 @@ processBind :: Attributes -> [Node] -> LarcenyM s [Text] processBind atr kids nextNodes = do - (LarcenyState pth m l o _ _) <- get + m <- _lSubs <$> get let tagName = atr M.! "tag" newSubs = subs [(tagName, Fill $ \_a _t ->do runTemplate (mk kids) m)] @@ -233,7 +231,7 @@ processApply :: Attributes -> [Node] -> LarcenyM s [Text] processApply atr kids = do - (LarcenyState pth m l o _ _) <- get + (LarcenyState pth m l _ _ _) <- get filledAttrs <- fillAttrs atr let (absolutePath, tplToApply) = findTemplateFromAttrs pth l filledAttrs contentTpl <- runTemplate (mk kids) m diff --git a/src/Web/Larceny/Types.hs b/src/Web/Larceny/Types.hs index a2f64d8..186938c 100644 --- a/src/Web/Larceny/Types.hs +++ b/src/Web/Larceny/Types.hs @@ -21,10 +21,11 @@ module Web.Larceny.Types ( Blank(..) , (.=) , lState , lSubs - , lPath) where + , lPath + , lLib) where import Control.Exception -import Control.Monad.State (StateT, evalStateT, get, runStateT, modify, MonadState) +import Control.Monad.State (StateT, get, runStateT, modify, MonadState) import Lens.Micro import Control.Monad.Trans (liftIO) import Data.Hashable (Hashable, hash, hashWithSalt) diff --git a/test/Spec.hs b/test/Spec.hs index b643dda..c75d6ea 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -110,7 +110,7 @@ removeSpaces = T.replace " " "" renderM :: Text -> LarcenyHspecM s Text renderM templateText = do - (LarcenyHspecState _ ls@(LarcenyState p s l o _ st)) <- S.get + (LarcenyHspecState _ ls@(LarcenyState _ s _ o _ _)) <- S.get let tpl = parseWithOverrides o (LT.fromStrict templateText) liftIO $ evalStateT (runTemplate tpl s) ls From c030ab66402b1fe82332096a481292e0381d08e2 Mon Sep 17 00:00:00 2001 From: Libby Horacek Date: Wed, 23 May 2018 14:16:17 -0400 Subject: [PATCH 13/14] Move all the spec lenses to Types for now --- src/Web/Larceny/Types.hs | 16 ++++++++-------- test/Spec.hs | 29 +++++++++-------------------- 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/src/Web/Larceny/Types.hs b/src/Web/Larceny/Types.hs index 186938c..846f585 100644 --- a/src/Web/Larceny/Types.hs +++ b/src/Web/Larceny/Types.hs @@ -19,10 +19,11 @@ module Web.Larceny.Types ( Blank(..) , LarcenyM , toLarcenyState , (.=) - , lState , lSubs , lPath - , lLib) where + , lLib + , lOverrides + , lAppState) where import Control.Exception import Control.Monad.State (StateT, get, runStateT, modify, MonadState) @@ -54,21 +55,20 @@ l .= b = modify (l .~ b) 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 }) - -lState :: Lens' (LarcenyState s) s -lState = lens _lAppState (\l s -> l { _lAppState = 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 }) toLarcenyState :: StateT s IO a -> LarcenyM s a toLarcenyState f = do l <- get (result, s') <- liftIO $ runStateT f (_lAppState l) - lState .= s' + lAppState .= s' return result -- End temporary diff --git a/test/Spec.hs b/test/Spec.hs index c75d6ea..39fa54e 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -1,17 +1,17 @@ +{-# 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, +import Control.Exception (ErrorCall (..), Exception, catch, + throw, try) +import Control.Monad.State (StateT (..), evalStateT, get, runStateT) import qualified Control.Monad.State as S import Control.Monad.Trans (liftIO) @@ -23,25 +23,14 @@ 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 (.=) #-} - -lPath :: Lens' (LarcenyState s) [Text] -lPath = lens _lPath (\ls p -> ls { _lPath = p }) -lSubs :: Lens' (LarcenyState s) (Substitutions s) -lSubs = lens _lSubs (\ls s -> ls { _lSubs = s }) -lLib :: Lens' (LarcenyState s) (Library s) -lLib = lens _lLib (\ls l -> ls { _lLib = l }) -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 }) + +import Web.Larceny +import Web.Larceny.Types (lAppState, lLib, lOverrides, lPath, + lSubs, (.=)) type LarcenyHspecM s = StateT (LarcenyHspecState s) IO From 6a246c4b152ae7106026f11847f93d9866a9d06a Mon Sep 17 00:00:00 2001 From: Libby Date: Sun, 7 Apr 2019 15:53:06 -0400 Subject: [PATCH 14/14] Remove modify function from Types module --- src/Web/Larceny/Fills.hs | 6 +++--- src/Web/Larceny/Internal.hs | 9 +++++---- src/Web/Larceny/Types.hs | 29 +++++++++++------------------ test/Spec.hs | 16 ++++++++++++++-- 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/Web/Larceny/Fills.hs b/src/Web/Larceny/Fills.hs index 2033d23..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 $ \_a _t -> HE.text <$> toLarcenyState t - +textFill' t = Fill $ \_a _t -> HE.text <$> toLarcenyState t -- | Use state or IO, then fill in some text. -- -- @ diff --git a/src/Web/Larceny/Internal.hs b/src/Web/Larceny/Internal.hs index e452805..b0fe57c 100644 --- a/src/Web/Larceny/Internal.hs +++ b/src/Web/Larceny/Internal.hs @@ -5,7 +5,8 @@ module Web.Larceny.Internal ( findTemplate , parseWithOverrides) where import Control.Exception -import Control.Monad.State (get) +import Lens.Micro ((.~)) +import Control.Monad.State (MonadState, modify, get) import Control.Monad.Trans (liftIO) import qualified Data.HashSet as HS import qualified Data.Map as M @@ -98,7 +99,7 @@ mk :: [Node] -> Template s mk = f where f nodes = Template $ \m -> - do lSubs .= m + do modify (lSubs .~ m) -- lPath .= pth T.concat <$> process nodes @@ -220,7 +221,7 @@ processBind atr kids nextNodes = do let tagName = atr M.! "tag" newSubs = subs [(tagName, Fill $ \_a _t ->do runTemplate (mk kids) m)] - lSubs .= newSubs `M.union` m + modify (lSubs .~ newSubs `M.union` m) process nextNodes -- Look up the template that's supposed to be applied in the library, @@ -237,7 +238,7 @@ processApply atr kids = do contentTpl <- runTemplate (mk kids) m let contentSub = subs [("apply-content", rawTextFill contentTpl)] - lPath .= absolutePath + modify (lPath .~ absolutePath) sequence [ runTemplate tplToApply (contentSub `M.union` m) ] findTemplateFromAttrs :: Path -> diff --git a/src/Web/Larceny/Types.hs b/src/Web/Larceny/Types.hs index 846f585..094fc82 100644 --- a/src/Web/Larceny/Types.hs +++ b/src/Web/Larceny/Types.hs @@ -18,7 +18,6 @@ module Web.Larceny.Types ( Blank(..) , LarcenyState(..) , LarcenyM , toLarcenyState - , (.=) , lSubs , lPath , lLib @@ -26,8 +25,8 @@ module Web.Larceny.Types ( Blank(..) , lAppState) where import Control.Exception -import Control.Monad.State (StateT, get, runStateT, modify, MonadState) -import Lens.Micro +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) @@ -47,12 +46,6 @@ data LarcenyState s = , _lLogger :: (Text -> IO ()) , _lAppState :: s } --- Temporary while transitioning to LarcenyM -infix 4 .= -(.=) :: MonadState s m => ASetter s s a b -> b -> m () -l .= b = modify (l .~ b) -{-# INLINE (.=) #-} - lSubs :: Lens' (LarcenyState s) (Substitutions s) lSubs = lens _lSubs (\l s -> l { _lSubs = s }) lPath :: Lens' (LarcenyState s) Path @@ -64,13 +57,13 @@ 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, s') <- liftIO $ runStateT f (_lAppState l) - lAppState .= s' - return result --- End temporary +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 @@ -97,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: @@ -107,7 +100,7 @@ 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 diff --git a/test/Spec.hs b/test/Spec.hs index 39fa54e..3dd418b 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -12,7 +12,7 @@ import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar) import Control.Exception (ErrorCall (..), Exception, catch, throw, try) import Control.Monad.State (StateT (..), evalStateT, get, - runStateT) + runStateT, modify, MonadState) import qualified Control.Monad.State as S import Control.Monad.Trans (liftIO) import qualified Data.Map as M @@ -30,7 +30,7 @@ import qualified Test.Hspec.Core.Spec as H import Web.Larceny import Web.Larceny.Types (lAppState, lLib, lOverrides, lPath, - lSubs, (.=)) + lSubs) type LarcenyHspecM s = StateT (LarcenyHspecState s) IO @@ -38,6 +38,11 @@ data LarcenyHspecState s = LarcenyHspecState { _hResult :: H.Result , _hLarcenyState :: LarcenyState s } +infix 4 .= +(.=) :: MonadState s m => ASetter s s a b -> b -> m () +l .= b = modify (l .~ b) +{-# INLINE (.=) #-} + hResult :: Lens' (LarcenyHspecState s) H.Result hResult = lens _hResult (\hs r -> hs { _hResult = r }) hLarcenyState :: Lens' (LarcenyHspecState s) (LarcenyState s) @@ -370,6 +375,13 @@ spec = hspec $ do "

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