-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | An implementation of the Jinja2 template language in Haskell -- -- Ginger is Jinja, minus the most blatant pythonisms. Wants to be -- feature complete, but isn't quite there yet. @package ginger @version 0.1.4.0 -- | A HTML type, useful for implementing type-safe conversion between -- plain text and HTML. The HTML representation used here assumed Unicode -- throughout, and UTF-8 should be used as the encoding when sending -- Html objects as responses to a HTTP client. module Text.Ginger.Html -- | A chunk of HTML source. data Html -- | Convert a chunk of HTML source code into an Html value as-is. -- Note that this bypasses any and all HTML encoding; the caller is -- responsible for taking appropriate measures against XSS and other -- potential vulnerabilities. In other words, the input to this function -- is considered pre-sanitized. unsafeRawHtml :: Text -> Html -- | Safely convert plain text to HTML. html :: Text -> Html -- | Extract HTML source code from an Html value. htmlSource :: Html -> Text -- | Types that support conversion to HTML. class ToHtml s toHtml :: ToHtml s => s -> Html instance GHC.Classes.Ord Text.Ginger.Html.Html instance GHC.Classes.Eq Text.Ginger.Html.Html instance GHC.Show.Show Text.Ginger.Html.Html instance GHC.Base.Monoid Text.Ginger.Html.Html instance Text.Ginger.Html.ToHtml Data.Text.Internal.Text instance Text.Ginger.Html.ToHtml [GHC.Types.Char] instance Text.Ginger.Html.ToHtml Text.Ginger.Html.Html -- | Implements Ginger's Abstract Syntax Tree. module Text.Ginger.AST -- | A context variable name. type VarName = Text -- | Top-level data structure, representing a fully parsed template. data Template Template :: Statement -> HashMap VarName Block -> Maybe Template -> Template [templateBody] :: Template -> Statement [templateBlocks] :: Template -> HashMap VarName Block [templateParent] :: Template -> Maybe Template -- | A macro definition ( {% macro %} ) data Macro Macro :: [VarName] -> Statement -> Macro [macroArgs] :: Macro -> [VarName] [macroBody] :: Macro -> Statement -- | A block definition ( {% block %} ) data Block Block :: Statement -> Block [blockBody] :: Block -> Statement -- | Ginger statements. data Statement -- | A sequence of multiple statements MultiS :: [Statement] -> Statement -- | Run wrapped statement in a local scope ScopedS :: Statement -> Statement -- | Literal output (anything outside of any tag) LiteralS :: Html -> Statement -- | {{ expression }} InterpolationS :: Expression -> Statement -- | {% if expression %}statement{% else %}statement{% endif %} IfS :: Expression -> Statement -> Statement -> Statement -- | {% for index, varname in expression %}statement{% endfor %} ForS :: (Maybe VarName) -> VarName -> Expression -> Statement -> Statement -- | {% set varname = expr %} SetVarS :: VarName -> Expression -> Statement -- | {% macro varname %}statements{% endmacro %} DefMacroS :: VarName -> Macro -> Statement BlockRefS :: VarName -> Statement -- | {% include "template" %} PreprocessedIncludeS :: Template -> Statement -- | The do-nothing statement (NOP) NullS :: Statement -- | Expressions, building blocks for the expression minilanguage. data Expression -- | String literal expression: "foobar" StringLiteralE :: Text -> Expression -- | Numeric literal expression: 123.4 NumberLiteralE :: Scientific -> Expression -- | Boolean literal expression: true BoolLiteralE :: Bool -> Expression -- | Literal null NullLiteralE :: Expression -- | Variable reference: foobar VarE :: VarName -> Expression -- | List construct: [ expr, expr, expr ] ListE :: [Expression] -> Expression -- | Object construct: { expr: expr, expr: expr, ... } ObjectE :: [(Expression, Expression)] -> Expression -- | foo[bar] (also dot access) MemberLookupE :: Expression -> Expression -> Expression -- | foo(bar=baz, quux) CallE :: Expression -> [(Maybe Text, Expression)] -> Expression instance GHC.Show.Show Text.Ginger.AST.Block instance GHC.Show.Show Text.Ginger.AST.Template instance GHC.Show.Show Text.Ginger.AST.Macro instance GHC.Show.Show Text.Ginger.AST.Statement instance GHC.Show.Show Text.Ginger.AST.Expression -- | GVal is a generic unitype value, representing the kind of values that -- Ginger can understand. -- -- Most of the types in this module are parametrized over an m -- type, which is the host monad for template execution, as passed to -- runGingerT. For most kinds of values, m is -- transparent, and in many cases a ToGVal instance can be written -- that works for all possible m; the reason we need to -- parametrize the values themselves over the carrier monad is because we -- want to support impure functions, which requires access to the -- underlying carrier monad (e.g. IO). module Text.Ginger.GVal -- | A variant type designed as the unitype for the template language. Any -- value referenced in a template, returned from within a template, or -- used in a template context, will be a GVal. m, in most -- cases, should be a Monad. -- -- | Some laws apply here, most notably: - when isNull is -- True, then all of asFunction, asText, -- asNumber, asHtml, asList, asDictItems, and -- length should produce Nothing - when isNull is -- True, then asBoolean should produce False - when -- asNumber is not Nothing, then asBoolean should -- only return False for exactly zero - Nothing-ness of -- length should match one or both of asList / -- asDictItems data GVal m GVal :: Maybe [GVal m] -> Maybe [(Text, GVal m)] -> Maybe (Text -> Maybe (GVal m)) -> Html -> Text -> Bool -> Maybe Scientific -> Maybe (Function m) -> Maybe Int -> Bool -> GVal m -- | Convert value to list, if possible [asList] :: GVal m -> Maybe [GVal m] -- | Convert value to association list ("dictionary"), if possible [asDictItems] :: GVal m -> Maybe [(Text, GVal m)] -- | Convert value to a lookup function [asLookup] :: GVal m -> Maybe (Text -> Maybe (GVal m)) -- | Render value as HTML [asHtml] :: GVal m -> Html -- | Render value as plain-text [asText] :: GVal m -> Text -- | Get value's truthiness [asBoolean] :: GVal m -> Bool -- | Convert value to a number, if possible [asNumber] :: GVal m -> Maybe Scientific -- | Access value as a callable function, if it is one [asFunction] :: GVal m -> Maybe (Function m) -- | Get length of value, if it is a collection (list/dict) [length] :: GVal m -> Maybe Int -- | Check if the value is null [isNull] :: GVal m -> Bool -- | The default GVal is equivalent to NULL. -- | For convenience, Show is implemented in a way that looks -- similar to JavaScript / JSON -- | Converting to HTML hooks into the ToHtml instance for Text for -- most tags. Tags that have no obvious textual representation render as -- empty HTML. -- | A function that can be called from within a template execution -- context. type Function m = [(Maybe Text, GVal m)] -> m (GVal m) -- | Match arguments passed to a function at runtime against a list of -- declared argument names. matchFuncArgs argNames argsPassed -- returns (matchedArgs, positionalArgs, namedArgs), where -- matchedArgs is a list of arguments matched against declared -- names (by name or by position), positionalArgs are the unused -- positional (unnamed) arguments, and namedArgs are the unused -- named arguments. matchFuncArgs :: [Text] -> [(Maybe Text, GVal m)] -> (HashMap Text (GVal m), [GVal m], HashMap Text (GVal m)) -- | Types that implement conversion to GVal. class ToGVal m a toGVal :: ToGVal m a => a -> GVal m -- | Trivial instance for GVal itself. -- | Nothing becomes NULL, Just unwraps. -- | Haskell lists become list-like GVals -- | HashMap of Text becomes a dictionary-like GVal -- | A key/value pair, used for constructing dictionary GVals using a -- compact syntax. type Pair m = (Text, GVal m) -- | Construct a dictionary GVal from a list of pairs. dict :: [Pair m] -> GVal m -- | Construct a pair from a key and a value. (~>) :: ToGVal m a => Text -> a -> Pair m -- | Silly helper function, needed to bypass the default Show -- instance of Scientific in order to make integral -- Scientifics look like integers. scientificToText :: Scientific -> Text -- | Booleans render as 1 or empty string, and otherwise behave as -- expected. -- | String -> GVal conversion uses the IsString -- class; because String is an alias for '[Char]', there is also -- a ToGVal instance for String, but it marshals strings -- as lists of characters, i.e., calling toGVal on a string -- produces a list of characters on the GVal side. -- | Single characters are treated as length-1 Texts. -- | This instance is slightly wrong; the asBoolean, -- asNumber, and asText methods all treat the HTML source -- as plain text. We do this to avoid parsing the HTML back into a -- Text (and dealing with possible parser errors); the reason this -- instance exists at all is that we still want to be able to pass -- pre-rendered HTML around sometimes, and as long as we don't call any -- numeric or string functions on it, everything is fine. When such HTML -- values accidentally do get used as strings, the HTML source will bleed -- into the visible text, but at least this will not introduce an XSS -- vulnerability. -- -- It is therefore recommended to avoid passing Html values into -- templates, and also to avoid calling any string functions on -- Html values inside templates (e.g. capturing macro output and -- then passing it through a textual filter). -- | Convert Aeson Values to GVals over an arbitrary host -- monad. Because JSON cannot represent functions, this conversion will -- never produce a Function. -- | Turn a Function into a GVal fromFunction :: Function m -> GVal m -- | Check if the given GVal is a list-like object isList :: GVal m -> Bool -- | Check if the given GVal is a dictionary-like object isDict :: GVal m -> Bool -- | Treat a GVal as a flat list and look up a value by integer -- index. If the value is not a List, or if the index exceeds the list -- length, return Nothing. lookupIndex :: Int -> GVal m -> Maybe (GVal m) -- | Helper function; look up a value by an integer index when the index -- may or may not be available. If no index is given, return -- Nothing. lookupIndexMay :: Maybe Int -> GVal m -> Maybe (GVal m) -- | Strictly-typed lookup: treat value as a dictionary-like object and -- look up the value at a given key. lookupKey :: Text -> GVal m -> Maybe (GVal m) -- | Loosely-typed lookup: try dictionary-style lookup first (treat index -- as a string, and container as a dictionary), if that doesn't yield -- anything (either because the index is not string-ish, or because the -- container doesn't provide dictionary-style access), try index-based -- lookup. lookupLoose :: GVal m -> GVal m -> Maybe (GVal m) -- | Treat a GVal as a dictionary and list all the keys, with no -- particular ordering. keys :: GVal m -> Maybe [Text] -- | Convert a GVal to a number. toNumber :: GVal m -> Maybe Scientific -- | Convert a GVal to an Int. The conversion will fail when -- the value is not numeric, and also if it is too large to fit in an -- Int. toInt :: GVal m -> Maybe Int -- | Loose cast to boolean. -- -- Numeric zero, empty strings, empty lists, empty objects, -- Null, and boolean False are considered falsy, anything -- else (including functions) is considered true-ish. toBoolean :: GVal m -> Bool -- | Dynamically cast to a function. This yields Just a -- Function if the value is a function, Nothing if it's -- not. toFunction :: GVal m -> Maybe (Function m) instance Data.Default.Class.Default (Text.Ginger.GVal.GVal m) instance GHC.Show.Show (Text.Ginger.GVal.GVal m) instance Text.Ginger.Html.ToHtml (Text.Ginger.GVal.GVal m) instance Text.Ginger.GVal.ToGVal m (Text.Ginger.GVal.GVal m) instance Text.Ginger.GVal.ToGVal m v => Text.Ginger.GVal.ToGVal m (GHC.Base.Maybe v) instance Text.Ginger.GVal.ToGVal m v => Text.Ginger.GVal.ToGVal m [v] instance Text.Ginger.GVal.ToGVal m v => Text.Ginger.GVal.ToGVal m (Data.HashMap.Base.HashMap Data.Text.Internal.Text v) instance Text.Ginger.GVal.ToGVal m GHC.Types.Int instance Text.Ginger.GVal.ToGVal m GHC.Integer.Type.Integer instance Text.Ginger.GVal.ToGVal m Data.Scientific.Scientific instance (Text.Ginger.GVal.ToGVal m a, Text.Ginger.GVal.ToGVal m b) => Text.Ginger.GVal.ToGVal m (a, b) instance (Text.Ginger.GVal.ToGVal m a, Text.Ginger.GVal.ToGVal m b, Text.Ginger.GVal.ToGVal m c) => Text.Ginger.GVal.ToGVal m (a, b, c) instance (Text.Ginger.GVal.ToGVal m a, Text.Ginger.GVal.ToGVal m b, Text.Ginger.GVal.ToGVal m c, Text.Ginger.GVal.ToGVal m d) => Text.Ginger.GVal.ToGVal m (a, b, c, d) instance Text.Ginger.GVal.ToGVal m GHC.Types.Bool instance Data.String.IsString (Text.Ginger.GVal.GVal m) instance Text.Ginger.GVal.ToGVal m GHC.Types.Char instance Text.Ginger.GVal.ToGVal m Data.Text.Internal.Text instance Text.Ginger.GVal.ToGVal m Data.Text.Internal.Lazy.Text instance Text.Ginger.GVal.ToGVal m Text.Ginger.Html.Html instance Text.Ginger.GVal.ToGVal m Data.Aeson.Types.Internal.Value -- | Execute Ginger templates in an arbitrary monad. -- -- Usage example: -- --
-- render :: Template -> Text -> Text -> Text -- render template -> username imageURL = do -- let contextLookup varName = -- case varName of -- "username" -> toGVal username -- "imageURL" -> toGVal imageURL -- _ -> def -- def for GVal is equivalent to a NULL value -- context = makeContext contextLookup -- in htmlSource $ runGinger context template --module Text.Ginger.Run -- | Monadically run a Ginger template. The m parameter is the -- carrier monad. runGingerT :: (Monad m, Functor m) => GingerContext m -> Template -> m () -- | Purely expand a Ginger template. The underlying carrier monad is -- Writer Html, which is used to collect the output and -- render it into a Html value. runGinger :: GingerContext (Writer Html) -> Template -> Html -- | Execution context. Determines how to look up variables from the -- environment, and how to write out template output. data GingerContext m -- | Create an execution context for runGinger. The argument is a lookup -- function that maps top-level context keys to ginger values. -- makeContext is a specialized version of makeContextM, -- targeting the Writer Html monad (which is what is used -- for the non-monadic template interpreter runGinger). -- -- The type of the lookup function may look intimidating, but in most -- cases, marshalling values from Haskell to Ginger is a matter of -- calling toGVal on them, so the 'GVal (Run (Writer Html))' part -- can usually be ignored. See the GVal module for details. makeContext :: (VarName -> GVal (Run (Writer Html))) -> GingerContext (Writer Html) -- | Create an execution context for runGingerT. Takes a lookup function, -- which returns ginger values into the carrier monad based on a lookup -- key, and a writer function (outputting HTML by whatever means the -- carrier monad provides, e.g. putStr for IO, or -- tell for Writers). makeContextM :: (Monad m, Functor m) => (VarName -> Run m (GVal (Run m))) -> (Html -> m ()) -> GingerContext m -- | Internal type alias for our template-runner monad stack. type Run m = StateT (RunState m) (ReaderT (GingerContext m) m) -- | Lift a value from the host monad m into the Run monad. liftRun :: Monad m => m a -> Run m a -- | Lift a function from the host monad m into the Run -- monad. liftRun2 :: Monad m => (a -> m b) -> a -> Run m b -- | Ginger parser. module Text.Ginger.Parse -- | Parse Ginger source from memory. parseGinger :: Monad m => IncludeResolver m -> Maybe SourceName -> Source -> m (Either ParserError Template) -- | Parse Ginger source from a file. parseGingerFile :: Monad m => IncludeResolver m -> SourceName -> m (Either ParserError Template) -- | Error information for Ginger parser errors. data ParserError ParserError :: String -> Maybe SourceName -> Maybe Int -> Maybe Int -> ParserError -- | Human-readable error message [peErrorMessage] :: ParserError -> String -- | Source name, if any [peSourceName] :: ParserError -> Maybe SourceName -- | Line number, if available [peSourceLine] :: ParserError -> Maybe Int -- | Column number, if available [peSourceColumn] :: ParserError -> Maybe Int -- | Used to resolve includes. Ginger will call this function whenever it -- encounters an {% include %}, {% import %}, or {% extends %} directive. -- If the required source code is not available, the resolver should -- return Nothing, else Just the source. type IncludeResolver m = SourceName -> m (Maybe Source) -- | Input type for the parser (source code). type Source = String -- | A source identifier (typically a filename). type SourceName = String instance GHC.Show.Show Text.Ginger.Parse.ParserError -- | A Haskell implementation of the Jinja2 template language. -- -- Ginger aims to be as close to the original Jinja language as possible, -- but avoiding blatant pythonisms and features that make little sense -- outside of an impure dynamic host language context, especially when -- this would require sacrificing runtime performance. module Text.Ginger