{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, NoMonomorphismRestriction,
ScopedTypeVariables, UndecidableInstances #-}{- |
Functions I found useful for doing webapps with HStringTemplate.
More usage examples can be found by grep -r \"Text.StringTemplate.Helpers\" in happs-tutorial, on hackage.
-}moduleText.StringTemplate.Helpers(directoryGroups',directoryGroups,directoryGroups2,dirgroupKeys,getTemplateGroup,renderTemplateDirGroup,lookupDirgroup,renderTemplateGroup,render1,STDirGroups,readTmplDef,readTmplM,readTmplTuples)whereimportText.StringTemplateimportText.StringTemplate.BaseimportSystem.DirectoryimportSystem.FilePathimportqualifiedSystem.IO.StrictasStrictimportControl.ApplicativeimportData.List(find)importData.CharimportControl.Monad.ReaderimportHSH(bracketCD)importqualifiedData.MapasMimportText.StringTemplate.ClassesimportSafeimportqualifiedSystem.FilePath.FindasFindimportSystem.FilePath{- |
Chooses a template from an STGroup, or errors if not found.
Render that template using attrs.
If a template k/v pair is repeated, it appears twice. (Perhaps a clue to buggy behavior?)
Repeated keys could be eliminated by running clean:
> clean = nubBy (\(a1,b1) (a2,b2) -> a1 == a2) . sortBy (\(a1,b1) (a2,b2) -> a1 `compare` a2)
The ToSElem type is probably either String or [String]
-}renderTemplateGroup::(ToSElema)=>STGroupString->[(String,a)]->[Char]->StringrenderTemplateGroupgrattrstmpl=maybe("template not found: "++tmpl)(toString.setManyAttribSaferattrs)(getStringTemplatetmplgr)renderTemplateGroupS::STGroupString->[(String,String)]->[Char]->StringrenderTemplateGroupS=renderTemplateGroup-- can this be done for Bytestrings? Below doesn't work, need an instance for (ToSElem B.ByteString)--renderTemplateGroupB :: STGroup String -> [(String, B.ByteString)] -> [Char] -> String--renderTemplateGroupB = renderTemplateGroup--t :: IO [FilePath]--t = do (map :: M.Map FilePath (STGroup String)) <- ( directoryGroupsSafer "/home/thartman/testtemplates" )-- return $ M.keys map--getSTtypeSTDirGroupsa=M.MapFilePath(STGroupa){- |
calculate a map of directory groups from a top-level directory
Each directory gives rise to its own groups.
Groups are independent; groups from higher in the directory structure do not have access to groups lower.
The top group has key \".\" (mnemonic, current directory), other groups have key names of subdirectories, including the starting ., eg \".\/templates\/path\/to/\subdir\"
-}directoryGroups=directoryGroups'directoryGroupdirectoryGroups'::(FilePath->IOa)->FilePath->IO(M.MapFilePatha)directoryGroups'f'd=bracketCDd$dosubDirs<-findDirectories$"."return.M.fromList=<<mapMfsubDirswherefd=dog<-f'dreturn(d,g)findDirectoriesd=Find.findFind.always(Find.fileTypeFind.==?Find.Directory)ddirectoryGroups2=directoryGroups'directoryGroup2{- | I think this does the same thing as directoryGroup (modulo IO strictness),
which uses an applicative idiom that melts my brain.
Not a direct translation, but it's easier for me to understand when written
Important change: readFile is strict. If it is left lazy, appkiller.sh causes happstutorial to crash
when in dynamicTemplateReload mode.
I think this needs to be fixed in HStringTemplate distribution as well.
-}directoryGroup2::Stringablea=>FilePath->IO(STGroupa)directoryGroup2path=dofs<-return.(filter((".st"==).takeExtension))=<<getDirectoryContentspathtemplates<-mapMgfsstmapping<-return.zip(mapdropExtensionfs)$templatesreturn$groupStringTemplatesstmapping-- For a while when debugging a problem in HAppS I thought this had to be-- System.IO.Strict.ReadFile, but now I think it makes no differencewheregf=docontents<-Strict.readFile$path</>freturn.newSTMP$contents{- |
The STGroup can't be shown in a useful way because it's a function type, but you can at least show the directories via Data.Map.keys.
-}dirgroupKeys::(Stringablea)=>STDirGroupsa->[FilePath]dirgroupKeys=M.keyslookupDirgroup::(Stringablea)=>FilePath->STDirGroupsa->Maybe(STGroupa)lookupDirgroupd=M.lookupd-- | > example: getTG "./baselayout" ts'getTemplateGroup::(Stringablea)=>FilePath->STDirGroupsa->STGroupagetTemplateGroupdirtdg=maybe(error$"getTG, bad dir:"++dir)id.lookupDirgroupdir$tdg-- | > example: renderTemplateDirGroup ts' "./baselayout" "base" renderTemplateDirGroup::ToSElema=>STDirGroupsString->FilePath->String->[(String,a)]->StringrenderTemplateDirGrouptdgdirtnameattrs=letts=getTemplateGroupdirtdginrenderTemplateGrouptsattrstnamesetManyAttribSaferattrsst=letmbFoundbadattr=findbadTmplVarName.mapfst$attrsinmaybe(setManyAttribattrsst)(\mbA->newSTMP.("setManyAttribSafer, bad template atr: "++)$mbA)mbFoundbadattr(<$$>)::(Functorf1,Functorf)=>(a->b)->f(f1a)->f(f1b)(<$$>)=(<$>).(<$>)badTmplVarName::String->BoolbadTmplVarNamet=or.map(not.isAlpha)$t{- |
> render1 [("name","Bill")] "Hi, my name is $name$"
> render1 attribs tmpl = render . setManyAttrib attribs . newSTMP $ tmpl
-}render1::[(String,String)]->String->Stringrender1attribstmpl=render.setManyAttribattribs.newSTMP$tmpl-- useful for HAppS, eg for dynamic menusreadTmplTuples::STGroupString->String->[(String,String)]readTmplTuples=readTmplDef[("readTutTuples error","")]readTmplDef::(Readb)=>b->STGroupString->FilePath->breadTmplDefdeftsf=either(constdef)id(readTmplMtsf::Reada=>EitherStringa)readTmplM::(Monadm,Reada)=>STGroupString->FilePath->mareadTmplMtsfile=safeRead.renderTemplateGroupts([]::[(String,String)]).concatMapescapequote$filewhereescapequotechar=ifchar=='"'then"\\\""else[char]safeRead::(Monadm,Reada)=>String->masafeReads=maybe(fail$"safeRead: "++s)return.readMay$s