packages feed

ginger 0.8.4.0 → 0.8.4.1

raw patch · 6 files changed

+214/−97 lines, 6 filesdep +optparse-applicativedep ~aesondep ~aeson-prettydep ~bytestringPVP: minor bump suggested

API additions: PVP suggests at least a minor version bump

Dependencies added: optparse-applicative

Dependency ranges changed: aeson, aeson-pretty, bytestring, data-default, mtl, process, tasty, tasty-hunit, tasty-quickcheck, text, time, transformers, utf8-string, vector, yaml

API changes (from Hackage documentation)

+ Text.Ginger.Run.Builtins: gfnLength :: Monad m => Function (Run p m h)

Files

cli/GingerCLI.hs view
@@ -19,6 +19,7 @@ import System.IO import System.IO.Error import qualified Data.ByteString as BS+import qualified Data.ByteString.UTF8 as UTF8 import qualified Data.ByteString.Lazy as LBS import Control.Monad.Trans.Class ( lift ) import Control.Monad.Trans.Maybe@@ -26,7 +27,85 @@ import Data.Default ( def ) import System.Process as Process import Text.Printf (printf)+import Options (parseOptions, Options (..), TemplateSource (..), DataSource (..))+import System.Exit +main :: IO ()+main = do+    args <- getArgs+    options <- parseOptions args+    case options of+      RunOptions tpl dat ->+        run tpl dat++loadData :: DataSource -> IO (Maybe (HashMap Text JSON.Value))+loadData (DataFromFile fn) = decodeFile fn+loadData DataFromStdin = decodeStdin+loadData (DataLiteral str) = decodeString str++loadTemplate :: TemplateSource -> IO (Template SourcePos)+loadTemplate tplSrc = do+    let resolve = loadFileMay+    (tpl, src) <- case tplSrc of+        TemplateFromFile fn -> (,) <$> parseGingerFile resolve fn <*> return Nothing+        TemplateFromStdin -> getContents >>= \s -> (,) <$> parseGinger resolve Nothing s <*> return (Just s)++    case tpl of+        Left err -> do+            tplSource <-+                case src of+                    Just s ->+                        return (Just s)+                    Nothing -> do+                        let s = sourceName <$> peSourcePosition err+                        case s of+                            Nothing -> return Nothing+                            Just sn -> Just <$> loadFile sn+            printParserError tplSource err+            exitFailure+        Right t -> do+            return t+++run :: TemplateSource -> DataSource -> IO ()+run tplSrc dataSrc = do+    scope <- loadData dataSrc+    let scopeLookup key = toGVal (scope >>= HashMap.lookup key)+    let contextLookup :: Text -> Run p IO Html (GVal (Run p IO Html))+        contextLookup key =+            case key of+                "print" -> return printF+                "system" -> return systemF+                _ -> return $ scopeLookup key+    let context =+            makeContextHtmlExM+                contextLookup+                (putStr . Text.unpack . htmlSource)+                (hPutStrLn stderr . show)++    tpl <- loadTemplate tplSrc+    runGingerT context tpl >>= either (hPutStrLn stderr . show) showOutput+    where+      showOutput value+        | isNull value = return ()+        | otherwise = putStrLn . show $ value+++printParserError :: Maybe String -> ParserError -> IO ()+printParserError srcMay = putStrLn . formatParserError srcMay++displayParserError :: String -> ParserError -> IO ()+displayParserError src pe = do+    case peSourcePosition pe of+        Just pos -> do+            let ln = Prelude.take 1 . Prelude.drop (sourceLine pos - 1) . Prelude.lines $ src+            case ln of+                [] -> return ()+                x:_ -> do+                    putStrLn x+                    putStrLn $ Prelude.replicate (sourceColumn pos - 1) ' ' ++ "^"+        _ -> return ()+ loadFile fn = openFile fn ReadMode >>= hGetContents  loadFileMay fn =@@ -40,6 +119,12 @@ decodeFile :: (JSON.FromJSON v) => FilePath -> IO (Maybe v) decodeFile fn = YAML.decode <$> (openFile fn ReadMode >>= BS.hGetContents) +decodeString :: (JSON.FromJSON v) => String -> IO (Maybe v)+decodeString = return . YAML.decode . UTF8.fromString++decodeStdin :: (JSON.FromJSON v) => IO (Maybe v)+decodeStdin = YAML.decode <$> BS.getContents+ printF :: GVal (Run p IO Html) printF = fromFunction $ go     where@@ -66,69 +151,3 @@ strToGVal :: String -> GVal m strToGVal = toGVal . Text.pack -main = do-    args <- getArgs-    let (srcFn, scopeFn) = case args of-            [] -> (Nothing, Nothing)-            a:[] -> (Just a, Nothing)-            a:b:[] -> (Just a, Just b)--    scope <- case scopeFn of-        Nothing -> return Nothing-        Just fn -> (decodeFile fn :: IO (Maybe (HashMap Text JSON.Value)))--    let scopeLookup key = toGVal (scope >>= HashMap.lookup key)-        resolve = loadFileMay-    let contextLookup :: Text -> Run p IO Html (GVal (Run p IO Html))-        contextLookup key =-            case key of-                "print" -> return printF-                "system" -> return systemF-                _ -> return $ scopeLookup key--    (tpl, src) <- case srcFn of-            Just fn -> (,) <$> parseGingerFile resolve fn <*> return Nothing-            Nothing -> getContents >>= \s -> (,) <$> parseGinger resolve Nothing s <*> return (Just s)--    -- TODO: do some sort of arg parsing thing so that we can turn-    -- template dumping on or off.-    -- print tpl--    case tpl of-        Left err -> do-            tplSource <--                case src of-                    Just s ->-                        return (Just s)-                    Nothing -> do-                        let s = sourceName <$> peSourcePosition err-                        case s of-                            Nothing -> return Nothing-                            Just sn -> Just <$> loadFile sn-            printParserError tplSource err-        Right t -> do-            let context =-                    makeContextHtmlExM-                        contextLookup-                        (putStr . Text.unpack . htmlSource)-                        (hPutStrLn stderr . show)-            runGingerT context t >>= either (hPutStrLn stderr . show) showOutput-    where-      showOutput value-        | isNull value = return ()-        | otherwise = putStrLn . show $ value--printParserError :: Maybe String -> ParserError -> IO ()-printParserError srcMay = putStrLn . formatParserError srcMay--displayParserError :: String -> ParserError -> IO ()-displayParserError src pe = do-    case peSourcePosition pe of-        Just pos -> do-            let ln = Prelude.take 1 . Prelude.drop (sourceLine pos - 1) . Prelude.lines $ src-            case ln of-                [] -> return ()-                x:_ -> do-                    putStrLn x-                    putStrLn $ Prelude.replicate (sourceColumn pos - 1) ' ' ++ "^"-        _ -> return ()
+ cli/Options.hs view
@@ -0,0 +1,69 @@+module Options+where++import Options.Applicative+++data TemplateSource+  = TemplateFromFile FilePath+  | TemplateFromStdin++data DataSource+  = DataFromFile FilePath+  | DataLiteral String+  | DataFromStdin++data Options+  = RunOptions TemplateSource DataSource++parseOptions :: [String] -> IO Options+parseOptions args =+  execParser $ info (options <**> helper)+    ( fullDesc+    <> header "ginger - A command-line interface for the Ginger template language"+    )++options :: Parser Options+options = runOptions++runOptions :: Parser Options+runOptions =+  RunOptions <$> templateSource <*> dataSource++templateSource :: Parser TemplateSource+templateSource =+  convert <$> option str+      ( long "template"+      <> short 't'+      <> metavar "TEMPLATE"+      <> help "Load ginger template from this file"+      <> value "-"+      )+  where+    convert "-" = TemplateFromStdin+    convert f = TemplateFromFile f++dataSource :: Parser DataSource+dataSource =+  dataFromFile <|> dataLiteral++dataFromFile :: Parser DataSource+dataFromFile =+  convert <$> option str+        ( long "data-file"+        <> metavar "DATAFILE"+        <> help "Load JSON or YAML data from this file (`-' to read from stdin)"+        )+  where+    convert "-" = DataFromStdin+    convert f = DataFromFile f++dataLiteral :: Parser DataSource+dataLiteral =+  DataLiteral <$> option str+        ( long "data"+        <> short 'd'+        <> metavar "DATA"+        <> help "Use specified (JSON or YAML) DATA"+        <> value "{}"+        )
ginger.cabal view
@@ -2,11 +2,11 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                ginger-version:             0.8.4.0+version:             0.8.4.1 synopsis:            An implementation of the Jinja2 template language in Haskell description:         Ginger is Jinja, minus the most blatant pythonisms. Wants                      to be feature complete, but isn't quite there yet.-homepage:            https://bitbucket.org/tdammers/ginger+homepage:            https://ginger.tobiasdammers.nl/ license:             MIT license-file:        LICENSE author:              Tobias Dammers@@ -20,6 +20,10 @@ cabal-version:       >=1.10 bug-reports:         https://github.com/tdammers/ginger/issues +source-repository head+    type: git+    location: https://github.com/tdammers/ginger+ library   exposed-modules: Text.Ginger                  , Text.Ginger.AST@@ -36,9 +40,9 @@   -- other-modules:   -- other-extensions:   build-depends: base >=4.8 && <5-               , aeson-               , aeson-pretty-               , bytestring+               , aeson >=1.4.2.0 && <1.5+               , aeson-pretty >=0.8.7 && <0.9+               , bytestring >=0.10.8.2 && <0.11                , data-default >= 0.5                , filepath >= 1.3                , http-types >= 0.8 && (< 0.11 || >= 0.12)@@ -47,12 +51,12 @@                , regex-tdfa >=1.2.3 && <=1.3                , safe >= 0.3                , scientific >= 0.3-               , text+               , text >=1.2.3.1 && <1.3                , time >= 0.1.6.0                , transformers >= 0.3                , unordered-containers >= 0.2.5-               , utf8-string-               , vector+               , utf8-string >=1.0.1.1 && <1.1+               , vector >=0.12.0.2 && <0.13   if !impl(ghc >= 8.0)     build-depends: semigroups == 0.18.*   hs-source-dirs:      src@@ -60,18 +64,21 @@  executable ginger     main-is: GingerCLI.hs+    other-modules: Options     hs-source-dirs: cli     default-language:    Haskell2010     build-depends: base >= 4.8 && <5-                 , bytestring-                 , data-default >= 0.5                  , ginger-                 , aeson-                 , process-                 , text-                 , transformers+                 , aeson >=1.4.2.0 && <1.5+                 , bytestring >=0.10.8.2 && <0.11+                 , data-default >= 0.5+                 , optparse-applicative >=0.14.3.0 && <0.15+                 , process >=1.6.5.0 && <1.7+                 , text >=1.2.3.1 && <1.3+                 , transformers >=0.5.6.2 && <0.6                  , unordered-containers >= 0.2.5-                 , yaml+                 , utf8-string >=1.0.1.1 && <1.1+                 , yaml >=0.11.0.0 && <0.12  test-suite tests     type: exitcode-stdio-1.0@@ -82,15 +89,15 @@     default-language: Haskell2010     build-depends: base >=4.8 && <5                  , ginger-                 , aeson-                 , bytestring-                 , data-default-                 , mtl-                 , tasty-                 , tasty-hunit-                 , tasty-quickcheck-                 , text-                 , time-                 , transformers+                 , aeson >=1.4.2.0 && <1.5+                 , bytestring >=0.10.8.2 && <0.11+                 , data-default >=0.5+                 , mtl >=2.2.2 && <2.3+                 , tasty >=1.2.1 && <1.3+                 , tasty-hunit >=0.10.0.1 && <0.11+                 , tasty-quickcheck >=0.10 && <0.11+                 , text >=1.2.3.1 && <1.3+                 , time >= 0.1.6.0+                 , transformers >=0.5.6.2 && <0.6                  , unordered-containers >= 0.2.5-                 , utf8-string+                 , utf8-string >=1.0.1.1 && <1.1
src/Text/Ginger/Run.hs view
@@ -166,7 +166,7 @@     , ("is_lt", fromFunction gfnLess)     , ("iterable", fromFunction . unaryFunc $ toGVal . (\x -> isList x || isDict x))     , ("json", fromFunction gfnJSON)-    , ("length", fromFunction . unaryFunc $ toGVal . length)+    , ("length", fromFunction gfnLength)     , ("le", fromFunction gfnLessEquals)     , ("less", fromFunction gfnLess)     , ("lessthan", fromFunction gfnLess)
src/Text/Ginger/Run/Builtins.hs view
@@ -272,6 +272,18 @@ gfnCenter ((_, s):(_, w):(_, pad):_) =     return . toGVal $ center (asText s) (fromMaybe 80 $ Prelude.truncate <$> asNumber w) (asText pad) +gfnLength :: Monad m => Function (Run p m h)+gfnLength [] =+  return def+gfnLength ((_,x):_) =+  case (asDictItems x, asList x) of+    (Nothing, Nothing) ->+      return . toGVal . Text.length . asText $ x+    (Just items, _) ->+      return . toGVal . Prelude.length $ items+    (_, Just items) ->+      return . toGVal . Prelude.length $ items+ gfnSlice :: Monad m => Function (Run p m h) gfnSlice args =     let argValues =
test/Text/Ginger/SimulationTests.hs view
@@ -745,10 +745,20 @@         -- TODO         -- \"iterable\"         -- TODO-        , testCase "\"length\"" $ do-            mkTestHtml [] []-                "{{ [1,2,3]|length }}"-                "3"+        , testGroup "\"length\""+            [ testCase "list" $ do+              mkTestHtml [] []+                  "{{ [1,2,3]|length }}"+                  "3"+            , testCase "dict" $ do+              mkTestHtml [] []+                  "{{ {'foo':'bar', 'baz':'quux'}|length }}"+                  "2"+            , testCase "string" $ do+              mkTestHtml [] []+                  "{{ 'foo'|length }}"+                  "3"+            ]         -- \"modulo\"         -- \"num\"         -- TODO