packages feed

stache 1.0.0 → 1.1.0

raw patch · 6 files changed

+119/−30 lines, 6 files

Files

CHANGELOG.md view
@@ -1,3 +1,10 @@+## Stache 1.1.0++* Added `mustacheRenderW` that allows to render a `Template` collecting+  warnings in the process. Also added the `MusthacheWarning` type+  representing the warnings themselves and `displayMustacheWarning` to+  pretty-print the warnings.+ ## Stache 1.0.0  * Improved metadata and documentation.
Text/Mustache/Render.hs view
@@ -15,29 +15,29 @@ {-# LANGUAGE OverloadedStrings #-}  module Text.Mustache.Render-  ( renderMustache )+  ( renderMustache+  , renderMustacheW ) where  import Control.Monad.Reader-import Control.Monad.Writer.Strict+import Control.Monad.State.Strict (State, modify', execState) import Data.Aeson import Data.Foldable (asum) import Data.List (tails) import Data.List.NonEmpty (NonEmpty (..))-import Data.Maybe (fromMaybe)+import Data.Semigroup ((<>)) import Data.Text (Text) import Text.Megaparsec.Pos (Pos, unPos) import Text.Mustache.Type-import qualified Data.ByteString.Lazy   as B-import qualified Data.HashMap.Strict    as H-import qualified Data.List.NonEmpty     as NE-import qualified Data.Map               as M-import qualified Data.Semigroup         as S-import qualified Data.Text              as T-import qualified Data.Text.Encoding     as T-import qualified Data.Text.Lazy         as TL-import qualified Data.Text.Lazy.Builder as B-import qualified Data.Vector            as V+import qualified Data.HashMap.Strict     as H+import qualified Data.List.NonEmpty      as NE+import qualified Data.Map                as M+import qualified Data.Semigroup          as S+import qualified Data.Text               as T+import qualified Data.Text.Lazy          as TL+import qualified Data.Text.Lazy.Builder  as B+import qualified Data.Text.Lazy.Encoding as TL+import qualified Data.Vector             as V  #if !MIN_VERSION_base(4,8,0) import Control.Applicative@@ -50,8 +50,10 @@ -- and accumulate the result as 'B.Builder' data which is then turned into a -- lazy 'TL.Text'. -type Render a = ReaderT RenderContext (Writer B.Builder) a+type Render a = ReaderT RenderContext (State S) a +data S = S ([MustacheWarning] -> [MustacheWarning]) B.Builder+ -- | The render monad context.  data RenderContext = RenderContext@@ -69,7 +71,14 @@ -- for interpolation.  renderMustache :: Template -> Value -> TL.Text-renderMustache t =+renderMustache t = snd . renderMustacheW t++-- | Like 'renderMustache', but also returns a collection of warnings.+--+-- @since 1.1.0++renderMustacheW :: Template -> Value -> ([MustacheWarning], TL.Text)+renderMustacheW t =   runRender (renderPartial (templateActual t) Nothing renderNode) t  -- | Render a single 'Node'.@@ -77,9 +86,9 @@ renderNode :: Node -> Render () renderNode (TextBlock txt) = outputIndented txt renderNode (EscapedVar k) =-  lookupKey k >>= outputRaw . escapeHtml . renderValue+  lookupKey k >>= renderValue k >>= outputRaw . escapeHtml renderNode (UnescapedVar k) =-  lookupKey k >>= outputRaw . renderValue+  lookupKey k >>= renderValue k >>= outputRaw renderNode (Section k ns) = do   val <- lookupKey k   enterSection k $@@ -103,9 +112,10 @@ -- | Run 'Render' monad given template to render and a 'Value' to take -- values from. -runRender :: Render a -> Template -> Value -> TL.Text-runRender m t v = (B.toLazyText . execWriter) (runReaderT m rc)+runRender :: Render a -> Template -> Value -> ([MustacheWarning], TL.Text)+runRender m t v = (ws [], B.toLazyText b)   where+    S ws b = execState (runReaderT m rc) (S id mempty)     rc = RenderContext       { rcIndent   = Nothing       , rcContext  = v :| []@@ -117,7 +127,7 @@ -- | Output a piece of strict 'Text'.  outputRaw :: Text -> Render ()-outputRaw = tell . B.fromText+outputRaw = tellBuilder . B.fromText {-# INLINE outputRaw #-}  -- | Output indentation consisting of appropriate number of spaces.@@ -185,7 +195,11 @@   v <- asks rcContext   p <- asks rcPrefix   let f x = asum (simpleLookup False (x <> k) <$> v)-  (return . fromMaybe Null . asum) (fmap (f . Key) . reverse . tails $ unKey p)+  case asum (fmap (f . Key) . reverse . tails $ unKey p) of+    Nothing ->+      Null <$ tellWarning (MustacheVariableNotFound (p <> k))+    Just r ->+      return r  -- | Lookup a 'Value' by traversing another 'Value' using given 'Key' as -- “path”.@@ -225,6 +239,16 @@ ---------------------------------------------------------------------------- -- Helpers +-- | Register a warning.++tellWarning :: MustacheWarning -> Render ()+tellWarning w = modify' $ \(S ws b) -> S (ws . (w:)) b++-- | Register a piece of output.++tellBuilder :: B.Builder -> Render ()+tellBuilder b' = modify' $ \(S ws b) -> S ws (b <> b')+ -- | Add two @'Maybe' 'Pos'@ values together.  addIndents :: Maybe Pos -> Maybe Pos -> Maybe Pos@@ -254,10 +278,20 @@  -- | Render Aeson's 'Value' /without/ HTML escaping. -renderValue :: Value -> Text-renderValue Null         = ""-renderValue (String str) = str-renderValue value        = (T.decodeUtf8 . B.toStrict . encode) value+renderValue :: Key -> Value -> Render Text+renderValue k v =+  case v of+    Null       -> return ""+    String str -> return str+    Object _ -> do+      tellWarning (MustacheDirectlyRenderedValue k)+      render v+    Array _ -> do+      tellWarning (MustacheDirectlyRenderedValue k)+      render v+    _ -> render v+  where+    render = return . TL.toStrict . TL.decodeUtf8 . encode {-# INLINE renderValue #-}  -- | Escape HTML represented as strict 'Text'.
Text/Mustache/Type.hs view
@@ -23,7 +23,9 @@   , Key (..)   , showKey   , PName (..)-  , MustacheException (..) )+  , MustacheException (..)+  , MustacheWarning (..)+  , displayMustacheWarning ) where  import Control.DeepSeq@@ -123,3 +125,26 @@ #else instance Exception MustacheException #endif++-- | Warning that may be generated during rendering of a 'Template'.+--+-- @since 1.1.0++data MustacheWarning+  = MustacheVariableNotFound Key+    -- ^ The template contained a variable for which there was no data+    -- counterpart in the current context.+  | MustacheDirectlyRenderedValue Key+    -- ^ A complex value such as an 'Object' or 'Array' was directly+    -- rendered into the template.+  deriving (Eq, Show, Typeable, Generic)++-- | Pretty-print a 'MustacheWarning'.+--+-- @since 1.1.0++displayMustacheWarning :: MustacheWarning -> String+displayMustacheWarning (MustacheVariableNotFound key) =+  "Referenced value was not provided, key: " ++ T.unpack (showKey key)+displayMustacheWarning (MustacheDirectlyRenderedValue key) =+  "Complex value rendered as such, key: " ++ T.unpack (showKey key)
stache.cabal view
@@ -1,5 +1,5 @@ name:                 stache-version:              1.0.0+version:              1.1.0 cabal-version:        >= 1.18 tested-with:          GHC==7.8.4, GHC==7.10.3, GHC==8.0.2, GHC==8.2.1 license:              BSD3
tests/Text/Mustache/RenderSpec.hs view
@@ -23,8 +23,11 @@  spec :: Spec spec = describe "renderMustache" $ do-  let r ns value =+  let w ns value =         let template = Template "test" (M.singleton "test" ns)+        in fst (renderMustacheW template value)+      r ns value =+        let template = Template "test" (M.singleton "test" ns)         in renderMustache template value       key = Key . pure   it "leaves text block “as is”" $@@ -37,11 +40,22 @@     r [UnescapedVar (key "foo")]       (object ["foo" .= ("<html>&\"something\"</html>" :: Text)])       `shouldBe` "<html>&\"something\"</html>"+  context "when rendering a variable" $ do+    context "when variable does not exist" $+      it "generates correct warning" $+        w [EscapedVar (key "foo")] (object []) `shouldBe`+          [MustacheVariableNotFound (key "foo")]+    context "when variable is not non-scalar" $+      it "generates correct warning" $+        w [EscapedVar (key "foo")] (object ["foo" .= object []]) `shouldBe`+          [MustacheDirectlyRenderedValue (key "foo")]   context "when rendering a section" $ do     let nodes = [Section (key "foo") [UnescapedVar (key "bar"), TextBlock "*"]]-    context "when the key is not present" $+    context "when the key is not present" $ do       it "renders nothing" $         r nodes (object []) `shouldBe` ""+      it "generates correct warning" $+        w nodes (object []) `shouldBe` [MustacheVariableNotFound (key "foo")]     context "when the key is present" $ do       context "when the key is a “false” value" $ do         it "skips the Null value" $@@ -96,9 +110,11 @@             `shouldBe` "x"   context "when rendering an inverted section" $ do     let nodes = [InvertedSection (key "foo") [TextBlock "Here!"]]-    context "when the key is not present" $+    context "when the key is not present" $ do       it "renders the inverse section" $         r nodes (object []) `shouldBe` "Here!"+      it "generates correct warning" $+        w nodes (object []) `shouldBe` [MustacheVariableNotFound (key "foo")]     context "when the key is present" $ do       context "when the key is a “false” value" $ do         it "renders with Null value" $
tests/Text/Mustache/TypeSpec.hs view
@@ -35,6 +35,13 @@         showKey (Key ["boo"]) `shouldBe` "boo"         showKey (Key ["foo","bar"]) `shouldBe` "foo.bar"         showKey (Key ["baz","baz","quux"]) `shouldBe` "baz.baz.quux"+  describe "displayMustacheWarning" $ do+    it "renders “not found” warning correctly" $+      displayMustacheWarning (MustacheVariableNotFound (Key ["foo"]))+        `shouldBe` "Referenced value was not provided, key: foo"+    it "renders “directly rendered value” warning correctly" $+      displayMustacheWarning (MustacheDirectlyRenderedValue (Key ["foo"]))+        `shouldBe` "Complex value rendered as such, key: foo"  templateA :: Template templateA = Template "a" $ M.fromList