yesod/yesod-static/Yesod/EmbeddedStatic/Generators.hs
John Lenz f8a35ce0a0 static: Add an embedded static subsite
This commit adds just the subsite itself.  The subsite
works by running a list of generaters at compile time.
The entries produced by the generators are converted into
wai-app-static.WaiAppStatic.Storage.Embedded entries.  Also,
addStaticContent is supported via an IORef.  When a widget
produces static content (css, javascript), it is stuck into
the IORef inside the embedded static subsite.  The embedded
static subsite will then serve it from the IORef, properly
using a 304 response if the client already has the content.
2013-09-12 12:21:33 -05:00

117 lines
3.7 KiB
Haskell

{-# LANGUAGE TemplateHaskell, QuasiQuotes, ScopedTypeVariables #-}
-- | A generator is executed at compile time to load a list of entries
-- to embed into the subsite. This module contains several basic generators,
-- but the design of generators and entries is such that it is straightforward
-- to make custom generators for your own specific purposes, see <#g:4 this section>.
module Yesod.EmbeddedStatic.Generators (
-- * Generators
Location
-- * Util
, pathToName
-- * Custom Generators
-- $example
) where
import Data.Char (isDigit, isLower)
import Language.Haskell.TH
import Yesod.EmbeddedStatic.Types
-- | Clean up a path to make it a valid haskell name by replacing all non-letters
-- and non-numbers by underscores. In addition, if the path starts with a capital
-- letter or number add an initial underscore.
pathToName :: FilePath -> Name
pathToName f = routeName
where
replace c
| 'A' <= c && c <= 'Z' = c
| 'a' <= c && c <= 'z' = c
| '0' <= c && c <= '9' = c
| otherwise = '_'
name = map replace f
routeName = mkName $
case () of
()
| null name -> error "null-named file"
| isDigit (head name) -> '_' : name
| isLower (head name) -> name
| otherwise -> '_' : name
-- $example
-- Here is an example of creating your own custom generator.
-- Because of template haskell stage restrictions, you must define generators in a
-- different module from where you use them. The following generator will embed a
-- JSON document that contains the compile time.
--
-- >{-# LANGUAGE TemplateHaskell, QuasiQuotes, OverloadedStrings #-}
-- >module CompileTime where
-- >
-- >import Data.Aeson
-- >import Data.Time
-- >import Yesod.EmbeddedStatic.Generators
-- >import Yesod.EmbeddedStatic.Types
-- >import qualified Data.ByteString.Lazy as BL
-- >
-- >getTime :: IO BL.ByteString
-- >getTime = do
-- > t <- getCurrentTime
-- > return $ encode $
-- > object [ "compile_time" .= show t ]
-- >
-- >timeGenerator :: Location -> Generator
-- >timeGenerator loc =
-- > return $ [Entry
-- > { ebHaskellName = Just $ pathToName loc
-- > , ebLocation = loc
-- > , ebMimeType = "application/json"
-- > , ebProductionContent = getTime
-- > , ebDevelReload = [| getTime |]
-- > , ebDevelExtraFiles = Nothing
-- > }]
--
-- Notice how the @getTime@ action is given as both 'ebProductionContent' and
-- 'ebDevelReload'. The result is that during development, the @getTime@ action
-- will be re-executed on every request so the time returned will be different
-- for each reload. When compiling for production, the @getTime@ action will
-- be executed once at compile time to produce the content to embed and never
-- called at runtime.
--
-- Here is a small example yesod program using this generator. Try toggling
-- the development argument to @mkEmbeddedStatic@.
--
-- >{-# LANGUAGE TemplateHaskell, QuasiQuotes, TypeFamilies #-}
-- >module Main where
-- >
-- >import Yesod
-- >import Yesod.EmbeddedStatic
-- >import CompileTime (timeGenerator)
-- >
-- >mkEmbeddedStatic True "eStatic" [timeGenerator "compile-time.json"]
-- >
-- >-- The above will generate variables
-- >-- eStatic :: EmbeddedStatic
-- >-- compile_time_json :: Route EmbeddedStatic
-- >
-- >data MyApp = MyApp { getStatic :: EmbeddedStatic }
-- >
-- >mkYesod "MyApp" [parseRoutes|
-- >/ HomeR GET
-- >/static StaticR EmbeddedStatic getStatic
-- >|]
-- >
-- >instance Yesod MyApp
-- >
-- >getHomeR :: Handler Html
-- >getHomeR = defaultLayout $ [whamlet|
-- ><h1>Hello
-- ><p>Check the
-- > <a href=@{StaticR compile_time_json}>compile time
-- >|]
-- >
-- >main :: IO ()
-- >main = warp 3000 $ MyApp eStatic