Scaffolder uses separate files

This commit is contained in:
Michael Snoyman 2010-09-20 09:24:42 +02:00
parent b13c08f001
commit 83368b05fd
25 changed files with 461 additions and 479 deletions

View File

@ -1,29 +1,24 @@
{-# LANGUAGE TemplateHaskell #-}
-- | A code generation quasi-quoter. Everything is taken as literal text, with ~var~ variable interpolation, and ~~ is completely ignored.
module CodeGenQ (codegen) where
-- | A code generation template haskell. Everything is taken as literal text,
-- with ~var~ variable interpolation.
module CodeGen (codegen) where
import Language.Haskell.TH.Quote
import Language.Haskell.TH.Syntax
import Text.ParserCombinators.Parsec
codegen :: QuasiQuoter
codegen = QuasiQuoter codegen' $ error "codegen cannot be a pattern"
import qualified System.IO.UTF8 as U
data Token = VarToken String | LitToken String | EmptyToken
codegen' :: String -> Q Exp
codegen' s' = do
let s = killFirstBlank s'
codegen :: FilePath -> Q Exp
codegen fp = do
s' <- qRunIO $ U.readFile $ "scaffold/" ++ fp ++ ".cg"
let s = init s'
case parse (many parseToken) s s of
Left e -> error $ show e
Right tokens' -> do
let tokens'' = map toExp tokens'
concat' <- [|concat|]
return $ concat' `AppE` ListE tokens''
where
killFirstBlank ('\n':x) = x
killFirstBlank ('\r':'\n':x) = x
killFirstBlank x = x
toExp :: Token -> Exp
toExp (LitToken s) = LitE $ StringL s

View File

@ -1,5 +1,5 @@
{-# LANGUAGE QuasiQuotes, TemplateHaskell #-}
import CodeGenQ
{-# LANGUAGE TemplateHaskell #-}
import CodeGen
import System.IO
import System.Directory
import qualified Data.ByteString.Char8 as S
@ -7,63 +7,35 @@ import Language.Haskell.TH.Syntax
main :: IO ()
main = do
putStr [$codegen|Welcome to the Yesod scaffolder.
I'm going to be creating a skeleton Yesod project for you.
What is your name? We're going to put this in the cabal and LICENSE files.
Your name: |]
putStr $(codegen "welcome")
hFlush stdout
name <- getLine
putStr [$codegen|
Welcome ~name~.
What do you want to call your project? We'll use this for the cabal name.
Project name: |]
putStr $(codegen "project-name")
hFlush stdout
project <- getLine
putStr [$codegen|
Now where would you like me to place your generated files? I'm smart enough
to create the directories, don't worry about that. If you leave this answer
blank, we'll place the files in ~project~.
Directory name: |]
putStr $(codegen "dir-name")
hFlush stdout
dirRaw <- getLine
let dir = if null dirRaw then project else dirRaw
putStr [$codegen|
Great, we'll be creating ~project~ today, and placing it in ~dir~.
What's going to be the name of your site argument datatype? This name must
start with a capital letter.
Site argument: |]
putStr $(codegen "site-arg")
hFlush stdout
sitearg <- getLine
putStr [$codegen|
That's it! I'm creating your files now...
|]
putStr [$codegen|
Yesod uses Persistent for its (you guessed it) persistence layer.
This tool will build in either SQLite or PostgreSQL support for you. If you
want to use a different backend, you'll have to make changes manually.
If you're not sure, stick with SQLite: it has no dependencies.
So, what'll it be? s for sqlite, p for postgresql: |]
putStr $(codegen "database")
hFlush stdout
backendS <- getLine
let pconn1 = [$codegen|user=~project~ password=~project~ host=localhost port=5432 dbname=~project~_debug|]
let pconn2 = [$codegen|user=~project~ password=~project~ host=localhost port=5432 dbname=~project~_production|]
let pconn1 = $(codegen "pconn1")
let pconn2 = $(codegen "pconn2")
let (lower, upper, connstr1, connstr2) =
case backendS of
"s" -> ("sqlite", "Sqlite", "debug.db3", "production.db3")
"p" -> ("postgresql", "Postgresql", pconn1, pconn2)
_ -> error $ "Invalid backend: " ++ backendS
putStrLn "That's it! I'm creating your files now..."
let writeFile' fp s = do
putStrLn $ "Generating " ++ fp
@ -75,433 +47,24 @@ So, what'll it be? s for sqlite, p for postgresql: |]
mkDir "cassius"
mkDir "julius"
writeFile' "simple-server.hs" [$codegen|
import Controller
import Network.Wai.Handler.SimpleServer (run)
main :: IO ()
main = putStrLn "Loaded" >> with~sitearg~ (run 3000)
|]
writeFile' "fastcgi.hs" [$codegen|
import Controller
import Network.Wai.Handler.FastCGI (run)
main :: IO ()
main = with~sitearg~ run
|]
writeFile' (project ++ ".cabal") [$codegen|
name: ~project~
version: 0.0.0
license: BSD3
license-file: LICENSE
author: ~name~
maintainer: ~name~
synopsis: The greatest Yesod web application ever.
description: I'm sure you can say something clever here if you try.
category: Web
stability: Experimental
cabal-version: >= 1.6
build-type: Simple
homepage: http://www.yesodweb.com/~project~
Flag production
Description: Build the production executable.
Default: False
executable simple-server
if flag(production)
Buildable: False
main-is: simple-server.hs
build-depends: base >= 4 && < 5,
yesod >= 0.5 && < 0.6,
wai-extra,
directory,
bytestring,
persistent,
persistent-~lower~,
template-haskell,
hamlet
ghc-options: -Wall
extensions: TemplateHaskell, QuasiQuotes, TypeFamilies
executable fastcgi
if flag(production)
Buildable: True
else
Buildable: False
cpp-options: -DPRODUCTION
main-is: fastcgi.hs
build-depends: wai-handler-fastcgi
ghc-options: -Wall
extensions: TemplateHaskell, QuasiQuotes, TypeFamilies
|]
writeFile' "LICENSE" [$codegen|
The following license covers this documentation, and the source code, except
where otherwise indicated.
Copyright 2010, ~name~. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|]
writeFile' (sitearg ++ ".hs") [$codegen|
{-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeFamilies #-}
module ~sitearg~
( ~sitearg~ (..)
, ~sitearg~Route (..)
, resources~sitearg~
, Handler
, maybeAuth
, requireAuth
, module Yesod
, module Settings
, module Model
, StaticRoute (..)
, AuthRoute (..)
) where
import Yesod
import Yesod.Mail
import Yesod.Helpers.Static
import Yesod.Helpers.Auth
import qualified Settings
import System.Directory
import qualified Data.ByteString.Lazy as L
import Yesod.WebRoutes
import Database.Persist.GenericSql
import Settings (hamletFile, cassiusFile, juliusFile)
import Model
import Control.Monad (join)
import Data.Maybe (isJust)
data ~sitearg~ = ~sitearg~
{ getStatic :: Static
, connPool :: Settings.ConnectionPool
}
type Handler = GHandler ~sitearg~ ~sitearg~
mkYesodData "~sitearg~" [$parseRoutes|
/static StaticR Static getStatic
/auth AuthR Auth getAuth
/favicon.ico FaviconR GET
/robots.txt RobotsR GET
/ RootR GET
|~~]
instance Yesod ~sitearg~ where
approot _ = Settings.approot
defaultLayout widget = do
mmsg <- getMessage
pc <- widgetToPageContent $ do
widget
addStyle $(Settings.cassiusFile "default-layout")
hamletToRepHtml $(Settings.hamletFile "default-layout")
urlRenderOverride a (StaticR s) =
Just $ uncurry (joinPath a Settings.staticroot) $ format s
where
format = formatPathSegments ss
ss :: Site StaticRoute (String -> Maybe (GHandler Static ~sitearg~ ChooseRep))
ss = getSubSite
urlRenderOverride _ _ = Nothing
authRoute _ = Just $ AuthR LoginR
addStaticContent ext' _ content = do
let fn = base64md5 content ++ '.' : ext'
let statictmp = Settings.staticdir ++ "/tmp/"
liftIO $ createDirectoryIfMissing True statictmp
liftIO $ L.writeFile (statictmp ++ fn) content
return $ Just $ Right (StaticR $ StaticRoute ["tmp", fn] [], [])
instance YesodPersist ~sitearg~ where
type YesodDB ~sitearg~ = SqlPersist
runDB db = fmap connPool getYesod >>= Settings.runConnectionPool db
instance YesodAuth ~sitearg~ where
type AuthEntity ~sitearg~ = User
type AuthEmailEntity ~sitearg~ = Email
defaultDest _ = RootR
getAuthId creds _extra = runDB $ do
x <- getBy $ UniqueUser $ credsIdent creds
case x of
Just (uid, _) -> return $ Just uid
Nothing -> do
fmap Just $ insert $ User (credsIdent creds) Nothing
openIdEnabled _ = True
emailSettings _ = Just EmailSettings
{ addUnverified = \email verkey ->
runDB $ insert $ Email email Nothing (Just verkey)
, sendVerifyEmail = sendVerifyEmail'
, getVerifyKey = runDB . fmap (join . fmap emailVerkey) . get
, setVerifyKey = \eid key -> runDB $ update eid [EmailVerkey $ Just key]
, verifyAccount = \eid -> runDB $ do
me <- get eid
case me of
Nothing -> return Nothing
Just e -> do
let email = emailEmail e
case emailUser e of
Just uid -> return $ Just uid
Nothing -> do
uid <- insert $ User email Nothing
update eid [EmailUser $ Just uid]
return $ Just uid
, getPassword = runDB . fmap (join . fmap userPassword) . get
, setPassword = \uid pass -> runDB $ update uid [UserPassword $ Just pass]
, getEmailCreds = \email -> runDB $ do
me <- getBy $ UniqueEmail email
case me of
Nothing -> return Nothing
Just (eid, e) -> return $ Just EmailCreds
{ emailCredsId = eid
, emailCredsAuthId = emailUser e
, emailCredsStatus = isJust $ emailUser e
, emailCredsVerkey = emailVerkey e
}
, getEmail = runDB . fmap (fmap emailEmail) . get
}
sendVerifyEmail' :: String -> String -> String -> GHandler Auth m ()
sendVerifyEmail' email _ verurl =
liftIO $ renderSendMail Mail
{ mailHeaders =
[ ("From", "noreply")
, ("To", email)
, ("Subject", "Verify your email address")
]
, mailPlain = verurl
, mailParts = return Part
{ partType = "text/html; charset=utf-8"
, partEncoding = None
, partDisposition = Inline
, partContent = renderHamlet id [$hamlet|
%p Please confirm your email address by clicking on the link below.
%p
%a!href=$verurl$ $verurl$
%p Thank you
|~~]
}
}
|]
writeFile' "Controller.hs" [$codegen|
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Controller
( with~sitearg~
) where
import ~sitearg~
import Settings
import Yesod.Helpers.Static
import Yesod.Helpers.Auth
import Database.Persist.GenericSql
import Handler.Root
mkYesodDispatch "~sitearg~" resources~sitearg~
getFaviconR :: Handler ()
getFaviconR = sendFile "image/x-icon" "favicon.ico"
getRobotsR :: Handler RepPlain
getRobotsR = return $ RepPlain $ toContent "User-agent: *"
with~sitearg~ :: (Application -> IO a) -> IO a
with~sitearg~ f = Settings.withConnectionPool $ \p -> do
flip runConnectionPool p $ runMigration $ do
migrate (undefined :: User)
migrate (undefined :: Email)
let h = ~sitearg~ s p
toWaiApp h >>= f
where
s = fileLookupDir Settings.staticdir typeByExt
|]
writeFile' "Handler/Root.hs" [$codegen|
{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}
module Handler.Root where
import ~sitearg~
getRootR :: Handler RepHtml
getRootR = do
mu <- maybeAuth
defaultLayout $ do
h2id <- newIdent
setTitle "~project~ homepage"
addBody $(hamletFile "homepage")
addStyle $(cassiusFile "homepage")
addJavascript $(juliusFile "homepage")
|]
writeFile' "Model.hs" [$codegen|
{-# LANGUAGE QuasiQuotes, TypeFamilies, GeneralizedNewtypeDeriving #-}
module Model where
import Yesod
mkPersist [$persist|
User
ident String
password String null update
UniqueUser ident
Email
email String
user UserId null update
verkey String null update
UniqueEmail email
|~~]
|]
writeFile' "Settings.hs" [$codegen|
{-# LANGUAGE CPP #-}
module Settings
( hamletFile
, cassiusFile
, juliusFile
, connStr
, ConnectionPool
, withConnectionPool
, runConnectionPool
, approot
, staticroot
, staticdir
) where
import qualified Text.Hamlet as H
import qualified Text.Cassius as H
import qualified Text.Julius as H
import Language.Haskell.TH.Syntax
import Database.Persist.~upper~
import Yesod (MonadCatchIO)
hamletFile :: FilePath -> Q Exp
#ifdef PRODUCTION
hamletFile x = H.hamletFile $ "hamlet/" ++ x ++ ".hamlet"
#else
hamletFile x = H.hamletFileDebug $ "hamlet/" ++ x ++ ".hamlet"
#endif
cassiusFile :: FilePath -> Q Exp
#ifdef PRODUCTION
cassiusFile x = H.cassiusFile $ "cassius/" ++ x ++ ".cassius"
#else
cassiusFile x = H.cassiusFileDebug $ "cassius/" ++ x ++ ".cassius"
#endif
juliusFile :: FilePath -> Q Exp
#ifdef PRODUCTION
juliusFile x = H.juliusFile $ "julius/" ++ x ++ ".julius"
#else
juliusFile x = H.juliusFileDebug $ "julius/" ++ x ++ ".julius"
#endif
connStr :: String
#ifdef PRODUCTION
connStr = "~connstr2~"
#else
connStr = "~connstr1~"
#endif
connectionCount :: Int
connectionCount = 10
withConnectionPool :: MonadCatchIO m => (ConnectionPool -> m a) -> m a
withConnectionPool = with~upper~Pool connStr connectionCount
runConnectionPool :: MonadCatchIO m => SqlPersist m a -> ConnectionPool -> m a
runConnectionPool = runSqlPool
approot :: String
#ifdef PRODUCTION
approot = "http://localhost:3000"
#else
approot = "http://localhost:3000"
#endif
staticroot :: String
staticroot = approot ++ "/static"
staticdir :: FilePath
staticdir = "static"
|]
writeFile' "cassius/default-layout.cassius" [$codegen|
body
font-family: sans-serif
|]
writeFile' "hamlet/default-layout.hamlet" [$codegen|
!!!
%html
%head
%title $pageTitle.pc$
^pageHead.pc^
%body
$maybe mmsg msg
#message $msg$
^pageBody.pc^
|]
writeFile' "hamlet/homepage.hamlet" [$codegen|
%h1 Hello
%h2#$h2id$ You do not have Javascript enabled.
$maybe mu u
%p
You are logged in as $userIdent.snd.u$. $
%a!href=@AuthR.LogoutR@ Logout
\.
$nothing
%p
You are not logged in. $
%a!href=@AuthR.LoginR@ Login now
\.
|]
writeFile' "cassius/homepage.cassius" [$codegen|
body
font-family: sans-serif
h1
text-align: center
h2#$h2id$
color: #990
|]
writeFile' "julius/homepage.julius" [$codegen|
window.onload = function(){
document.getElementById("%h2id%").innerHTML = "<i>Added from JavaScript.</i>";
}
|]
writeFile' "simple-server.hs" $(codegen "simple-server_hs")
writeFile' "fastcgi.hs" $(codegen "fastcgi_hs")
writeFile' (project ++ ".cabal") $(codegen "cabal")
writeFile' "LICENSE" $(codegen "LICENSE")
writeFile' (sitearg ++ ".hs") $(codegen "sitearg_hs")
writeFile' "Controller.hs" $(codegen "Controller_hs")
writeFile' "Handler/Root.hs" $(codegen "Root_hs")
writeFile' "Model.hs" $(codegen "Model_hs")
writeFile' "Settings.hs" $(codegen "Settings_hs")
writeFile' "cassius/default-layout.cassius"
$(codegen "default-layout_cassius")
writeFile' "hamlet/default-layout.hamlet"
$(codegen "default-layout_hamlet")
writeFile' "hamlet/homepage.hamlet" $(codegen "homepage_hamlet")
writeFile' "cassius/homepage.cassius" $(codegen "homepage_cassius")
writeFile' "julius/homepage.julius" $(codegen "homepage_julius")
S.writeFile (dir ++ "/favicon.ico")
$(runIO (S.readFile "favicon.ico") >>= \bs -> do
$(runIO (S.readFile "scaffold/favicon_ico.cg") >>= \bs -> do
pack <- [|S.pack|]
return $ pack `AppE` LitE (StringL $ S.unpack bs))

32
scaffold/Controller_hs.cg Normal file
View File

@ -0,0 +1,32 @@
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Controller
( with~sitearg~
) where
import ~sitearg~
import Settings
import Yesod.Helpers.Static
import Yesod.Helpers.Auth
import Database.Persist.GenericSql
import Handler.Root
mkYesodDispatch "~sitearg~" resources~sitearg~
getFaviconR :: Handler ()
getFaviconR = sendFile "image/x-icon" "favicon.ico"
getRobotsR :: Handler RepPlain
getRobotsR = return $ RepPlain $ toContent "User-agent: *"
with~sitearg~ :: (Application -> IO a) -> IO a
with~sitearg~ f = Settings.withConnectionPool $ \p -> do
flip runConnectionPool p $ runMigration $ do
migrate (undefined :: User)
migrate (undefined :: Email)
let h = ~sitearg~ s p
toWaiApp h >>= f
where
s = fileLookupDir Settings.staticdir typeByExt

26
scaffold/LICENSE.cg Normal file
View File

@ -0,0 +1,26 @@
The following license covers this documentation, and the source code, except
where otherwise indicated.
Copyright 2010, ~name~. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

17
scaffold/Model_hs.cg Normal file
View File

@ -0,0 +1,17 @@
{-# LANGUAGE QuasiQuotes, TypeFamilies, GeneralizedNewtypeDeriving #-}
module Model where
import Yesod
mkPersist [$persist|
User
ident String
password String null update
UniqueUser ident
Email
email String
user UserId null update
verkey String null update
UniqueEmail email
|~~]

15
scaffold/Root_hs.cg Normal file
View File

@ -0,0 +1,15 @@
{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}
module Handler.Root where
import ~sitearg~
getRootR :: Handler RepHtml
getRootR = do
mu <- maybeAuth
defaultLayout $ do
h2id <- newIdent
setTitle "~project~ homepage"
addBody $(hamletFile "homepage")
addStyle $(cassiusFile "homepage")
addJavascript $(juliusFile "homepage")

71
scaffold/Settings_hs.cg Normal file
View File

@ -0,0 +1,71 @@
{-# LANGUAGE CPP #-}
module Settings
( hamletFile
, cassiusFile
, juliusFile
, connStr
, ConnectionPool
, withConnectionPool
, runConnectionPool
, approot
, staticroot
, staticdir
) where
import qualified Text.Hamlet as H
import qualified Text.Cassius as H
import qualified Text.Julius as H
import Language.Haskell.TH.Syntax
import Database.Persist.~upper~
import Yesod (MonadCatchIO)
hamletFile :: FilePath -> Q Exp
#ifdef PRODUCTION
hamletFile x = H.hamletFile $ "hamlet/" ++ x ++ ".hamlet"
#else
hamletFile x = H.hamletFileDebug $ "hamlet/" ++ x ++ ".hamlet"
#endif
cassiusFile :: FilePath -> Q Exp
#ifdef PRODUCTION
cassiusFile x = H.cassiusFile $ "cassius/" ++ x ++ ".cassius"
#else
cassiusFile x = H.cassiusFileDebug $ "cassius/" ++ x ++ ".cassius"
#endif
juliusFile :: FilePath -> Q Exp
#ifdef PRODUCTION
juliusFile x = H.juliusFile $ "julius/" ++ x ++ ".julius"
#else
juliusFile x = H.juliusFileDebug $ "julius/" ++ x ++ ".julius"
#endif
connStr :: String
#ifdef PRODUCTION
connStr = "~connstr2~"
#else
connStr = "~connstr1~"
#endif
connectionCount :: Int
connectionCount = 10
withConnectionPool :: MonadCatchIO m => (ConnectionPool -> m a) -> m a
withConnectionPool = with~upper~Pool connStr connectionCount
runConnectionPool :: MonadCatchIO m => SqlPersist m a -> ConnectionPool -> m a
runConnectionPool = runSqlPool
approot :: String
#ifdef PRODUCTION
approot = "http://localhost:3000"
#else
approot = "http://localhost:3000"
#endif
staticroot :: String
staticroot = approot ++ "/static"
staticdir :: FilePath
staticdir = "static"

45
scaffold/cabal.cg Normal file
View File

@ -0,0 +1,45 @@
name: ~project~
version: 0.0.0
license: BSD3
license-file: LICENSE
author: ~name~
maintainer: ~name~
synopsis: The greatest Yesod web application ever.
description: I'm sure you can say something clever here if you try.
category: Web
stability: Experimental
cabal-version: >= 1.6
build-type: Simple
homepage: http://www.yesodweb.com/~project~
Flag production
Description: Build the production executable.
Default: False
executable simple-server
if flag(production)
Buildable: False
main-is: simple-server.hs
build-depends: base >= 4 && < 5,
yesod >= 0.5 && < 0.6,
wai-extra,
directory,
bytestring,
persistent,
persistent-~lower~,
template-haskell,
hamlet
ghc-options: -Wall
extensions: TemplateHaskell, QuasiQuotes, TypeFamilies
executable fastcgi
if flag(production)
Buildable: True
else
Buildable: False
cpp-options: -DPRODUCTION
main-is: fastcgi.hs
build-depends: wai-handler-fastcgi
ghc-options: -Wall
extensions: TemplateHaskell, QuasiQuotes, TypeFamilies

6
scaffold/database.cg Normal file
View File

@ -0,0 +1,6 @@
Yesod uses Persistent for its (you guessed it) persistence layer.
This tool will build in either SQLite or PostgreSQL support for you. If you
want to use a different backend, you'll have to make changes manually.
If you're not sure, stick with SQLite: it has no dependencies.
So, what'll it be? s for sqlite, p for postgresql:

View File

@ -0,0 +1,3 @@
body
font-family: sans-serif

View File

@ -0,0 +1,10 @@
!!!
%html
%head
%title $pageTitle.pc$
^pageHead.pc^
%body
$maybe mmsg msg
#message $msg$
^pageBody.pc^

5
scaffold/dir-name.cg Normal file
View File

@ -0,0 +1,5 @@
Now where would you like me to place your generated files? I'm smart enough
to create the directories, don't worry about that. If you leave this answer
blank, we'll place the files in ~project~.
Directory name:

6
scaffold/fastcgi_hs.cg Normal file
View File

@ -0,0 +1,6 @@
import Controller
import Network.Wai.Handler.FastCGI (run)
main :: IO ()
main = with~sitearg~ run

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,5 @@
h1
text-align: center
h2#$h2id$
color: #990

View File

@ -0,0 +1,13 @@
%h1 Hello
%h2#$h2id$ You do not have Javascript enabled.
$maybe mu u
%p
You are logged in as $userIdent.snd.u$. $
%a!href=@AuthR.LogoutR@ Logout
\.
$nothing
%p
You are not logged in. $
%a!href=@AuthR.LoginR@ Login now
\.

View File

@ -0,0 +1,4 @@
window.onload = function(){
document.getElementById("%h2id%").innerHTML = "<i>Added from JavaScript.</i>";
}

1
scaffold/pconn1.cg Normal file
View File

@ -0,0 +1 @@
user=~project~ password=~project~ host=localhost port=5432 dbname=~project~_debug

1
scaffold/pconn2.cg Normal file
View File

@ -0,0 +1 @@
user=~project~ password=~project~ host=localhost port=5432 dbname=~project~_production

4
scaffold/project-name.cg Normal file
View File

@ -0,0 +1,4 @@
Welcome ~name~.
What do you want to call your project? We'll use this for the cabal name.
Project name:

View File

@ -0,0 +1,6 @@
import Controller
import Network.Wai.Handler.SimpleServer (run)
main :: IO ()
main = putStrLn "Loaded" >> with~sitearg~ (run 3000)

5
scaffold/site-arg.cg Normal file
View File

@ -0,0 +1,5 @@
Great, we'll be creating ~project~ today, and placing it in ~dir~.
What's going to be the name of your site argument datatype? This name must
start with a capital letter.
Site argument:

143
scaffold/sitearg_hs.cg Normal file
View File

@ -0,0 +1,143 @@
{-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeFamilies #-}
module ~sitearg~
( ~sitearg~ (..)
, ~sitearg~Route (..)
, resources~sitearg~
, Handler
, maybeAuth
, requireAuth
, module Yesod
, module Settings
, module Model
, StaticRoute (..)
, AuthRoute (..)
) where
import Yesod
import Yesod.Mail
import Yesod.Helpers.Static
import Yesod.Helpers.Auth
import qualified Settings
import System.Directory
import qualified Data.ByteString.Lazy as L
import Yesod.WebRoutes
import Database.Persist.GenericSql
import Settings (hamletFile, cassiusFile, juliusFile)
import Model
import Control.Monad (join)
import Data.Maybe (isJust)
data ~sitearg~ = ~sitearg~
{ getStatic :: Static
, connPool :: Settings.ConnectionPool
}
type Handler = GHandler ~sitearg~ ~sitearg~
mkYesodData "~sitearg~" [$parseRoutes|
/static StaticR Static getStatic
/auth AuthR Auth getAuth
/favicon.ico FaviconR GET
/robots.txt RobotsR GET
/ RootR GET
|~~]
instance Yesod ~sitearg~ where
approot _ = Settings.approot
defaultLayout widget = do
mmsg <- getMessage
pc <- widgetToPageContent $ do
widget
addStyle $(Settings.cassiusFile "default-layout")
hamletToRepHtml $(Settings.hamletFile "default-layout")
urlRenderOverride a (StaticR s) =
Just $ uncurry (joinPath a Settings.staticroot) $ format s
where
format = formatPathSegments ss
ss :: Site StaticRoute (String -> Maybe (GHandler Static ~sitearg~ ChooseRep))
ss = getSubSite
urlRenderOverride _ _ = Nothing
authRoute _ = Just $ AuthR LoginR
addStaticContent ext' _ content = do
let fn = base64md5 content ++ '.' : ext'
let statictmp = Settings.staticdir ++ "/tmp/"
liftIO $ createDirectoryIfMissing True statictmp
liftIO $ L.writeFile (statictmp ++ fn) content
return $ Just $ Right (StaticR $ StaticRoute ["tmp", fn] [], [])
instance YesodPersist ~sitearg~ where
type YesodDB ~sitearg~ = SqlPersist
runDB db = fmap connPool getYesod >>= Settings.runConnectionPool db
instance YesodAuth ~sitearg~ where
type AuthEntity ~sitearg~ = User
type AuthEmailEntity ~sitearg~ = Email
defaultDest _ = RootR
getAuthId creds _extra = runDB $ do
x <- getBy $ UniqueUser $ credsIdent creds
case x of
Just (uid, _) -> return $ Just uid
Nothing -> do
fmap Just $ insert $ User (credsIdent creds) Nothing
openIdEnabled _ = True
emailSettings _ = Just EmailSettings
{ addUnverified = \email verkey ->
runDB $ insert $ Email email Nothing (Just verkey)
, sendVerifyEmail = sendVerifyEmail'
, getVerifyKey = runDB . fmap (join . fmap emailVerkey) . get
, setVerifyKey = \eid key -> runDB $ update eid [EmailVerkey $ Just key]
, verifyAccount = \eid -> runDB $ do
me <- get eid
case me of
Nothing -> return Nothing
Just e -> do
let email = emailEmail e
case emailUser e of
Just uid -> return $ Just uid
Nothing -> do
uid <- insert $ User email Nothing
update eid [EmailUser $ Just uid]
return $ Just uid
, getPassword = runDB . fmap (join . fmap userPassword) . get
, setPassword = \uid pass -> runDB $ update uid [UserPassword $ Just pass]
, getEmailCreds = \email -> runDB $ do
me <- getBy $ UniqueEmail email
case me of
Nothing -> return Nothing
Just (eid, e) -> return $ Just EmailCreds
{ emailCredsId = eid
, emailCredsAuthId = emailUser e
, emailCredsStatus = isJust $ emailUser e
, emailCredsVerkey = emailVerkey e
}
, getEmail = runDB . fmap (fmap emailEmail) . get
}
sendVerifyEmail' :: String -> String -> String -> GHandler Auth m ()
sendVerifyEmail' email _ verurl =
liftIO $ renderSendMail Mail
{ mailHeaders =
[ ("From", "noreply")
, ("To", email)
, ("Subject", "Verify your email address")
]
, mailPlain = verurl
, mailParts = return Part
{ partType = "text/html; charset=utf-8"
, partEncoding = None
, partDisposition = Inline
, partContent = renderHamlet id [$hamlet|
%p Please confirm your email address by clicking on the link below.
%p
%a!href=$verurl$ $verurl$
%p Thank you
|~~]
}
}

6
scaffold/welcome.cg Normal file
View File

@ -0,0 +1,6 @@
Welcome to the Yesod scaffolder.
I'm going to be creating a skeleton Yesod project for you.
What is your name? We're going to put this in the cabal and LICENSE files.
Your name:

View File

@ -14,7 +14,7 @@ stability: Stable
cabal-version: >= 1.6
build-type: Simple
homepage: http://docs.yesodweb.com/yesod/
extra-source-files: favicon.ico
extra-source-files: scaffold/*.cg
flag buildtests
description: Build the executable to run unit tests
@ -79,7 +79,7 @@ executable yesod
build-depends: parsec >= 2.1 && < 4
ghc-options: -Wall
main-is: scaffold.hs
other-modules: CodeGenQ
other-modules: CodeGen
extensions: TemplateHaskell
executable runtests