Scaffolding update
This commit is contained in:
parent
9a3a2a5aa5
commit
f9ee741b92
@ -1,3 +1,11 @@
|
|||||||
|
## 1.4.3.4
|
||||||
|
|
||||||
|
Scaffolding updates:
|
||||||
|
|
||||||
|
* Improve `DevelMain` support
|
||||||
|
* Wipe out database during test runs
|
||||||
|
* Convenience `unsafeHandler` function
|
||||||
|
|
||||||
## 1.4.3.3
|
## 1.4.3.3
|
||||||
|
|
||||||
More consistent whitespace in hamlet files in scaffolding [#50](https://github.com/yesodweb/yesod-scaffold/issues/50)
|
More consistent whitespace in hamlet files in scaffolding [#50](https://github.com/yesodweb/yesod-scaffold/issues/50)
|
||||||
|
|||||||
@ -32,15 +32,22 @@ module Application
|
|||||||
, appMain
|
, appMain
|
||||||
, develMain
|
, develMain
|
||||||
, makeFoundation
|
, makeFoundation
|
||||||
|
-- * for DevelMain
|
||||||
|
, getApplicationRepl
|
||||||
|
, shutdownApp
|
||||||
|
-- * for GHCI
|
||||||
|
, handler
|
||||||
|
, db
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Monad.Logger (liftLoc)
|
import Control.Monad.Logger (liftLoc)
|
||||||
|
import Database.Persist.MongoDB (MongoContext)
|
||||||
import Import
|
import Import
|
||||||
import Language.Haskell.TH.Syntax (qLocation)
|
import Language.Haskell.TH.Syntax (qLocation)
|
||||||
import Network.Wai.Handler.Warp (Settings, defaultSettings,
|
import Network.Wai.Handler.Warp (Settings, defaultSettings,
|
||||||
defaultShouldDisplayException,
|
defaultShouldDisplayException,
|
||||||
runSettings, setHost,
|
runSettings, setHost,
|
||||||
setOnException, setPort)
|
setOnException, setPort, getPort)
|
||||||
import Network.Wai.Middleware.RequestLogger (Destination (Logger),
|
import Network.Wai.Middleware.RequestLogger (Destination (Logger),
|
||||||
IPAddrSource (..),
|
IPAddrSource (..),
|
||||||
OutputFormat (..), destination,
|
OutputFormat (..), destination,
|
||||||
@ -115,12 +122,15 @@ warpSettings foundation =
|
|||||||
-- | For yesod devel, return the Warp settings and WAI Application.
|
-- | For yesod devel, return the Warp settings and WAI Application.
|
||||||
getApplicationDev :: IO (Settings, Application)
|
getApplicationDev :: IO (Settings, Application)
|
||||||
getApplicationDev = do
|
getApplicationDev = do
|
||||||
settings <- loadAppSettings [configSettingsYml] [] useEnv
|
settings <- getAppSettings
|
||||||
foundation <- makeFoundation settings
|
foundation <- makeFoundation settings
|
||||||
app <- makeApplication foundation
|
|
||||||
wsettings <- getDevSettings $ warpSettings foundation
|
wsettings <- getDevSettings $ warpSettings foundation
|
||||||
|
app <- makeApplication foundation
|
||||||
return (wsettings, app)
|
return (wsettings, app)
|
||||||
|
|
||||||
|
getAppSettings :: IO AppSettings
|
||||||
|
getAppSettings = loadAppSettings [configSettingsYml] [] useEnv
|
||||||
|
|
||||||
-- | main function for use by yesod devel
|
-- | main function for use by yesod devel
|
||||||
develMain :: IO ()
|
develMain :: IO ()
|
||||||
develMain = develMainHelper getApplicationDev
|
develMain = develMainHelper getApplicationDev
|
||||||
@ -145,16 +155,45 @@ appMain = do
|
|||||||
-- Run the application with Warp
|
-- Run the application with Warp
|
||||||
runSettings (warpSettings foundation) app
|
runSettings (warpSettings foundation) app
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------------------------------------------
|
||||||
|
-- Functions for DevelMain.hs (a way to run the app from GHCi)
|
||||||
|
--------------------------------------------------------------
|
||||||
|
getApplicationRepl :: IO (Int, App, Application)
|
||||||
|
getApplicationRepl = do
|
||||||
|
settings <- getAppSettings
|
||||||
|
foundation <- makeFoundation settings
|
||||||
|
wsettings <- getDevSettings $ warpSettings foundation
|
||||||
|
app1 <- makeApplication foundation
|
||||||
|
return (getPort wsettings, foundation, app1)
|
||||||
|
|
||||||
|
shutdownApp :: App -> IO ()
|
||||||
|
shutdownApp _ = return ()
|
||||||
|
|
||||||
|
|
||||||
|
---------------------------------------------
|
||||||
|
-- Functions for use in development with GHCi
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
-- | Run a handler
|
||||||
|
handler :: Handler a -> IO a
|
||||||
|
handler h = getAppSettings >>= makeFoundation >>= flip unsafeHandler h
|
||||||
|
|
||||||
|
-- | Run DB queries
|
||||||
|
db :: ReaderT MongoContext (HandlerT App IO) a -> IO a
|
||||||
|
db = handler . runDB
|
||||||
|
|
||||||
{-# START_FILE Foundation.hs #-}
|
{-# START_FILE Foundation.hs #-}
|
||||||
module Foundation where
|
module Foundation where
|
||||||
|
|
||||||
import Database.Persist.MongoDB hiding (master)
|
import Database.Persist.MongoDB hiding (master)
|
||||||
import Import.NoFoundation
|
import Import.NoFoundation
|
||||||
import Text.Hamlet (hamletFile)
|
import Text.Hamlet (hamletFile)
|
||||||
import Text.Jasmine (minifym)
|
import Text.Jasmine (minifym)
|
||||||
import Yesod.Auth.BrowserId (authBrowserId)
|
import Yesod.Auth.BrowserId (authBrowserId)
|
||||||
import Yesod.Core.Types (Logger)
|
import Yesod.Core.Types (Logger)
|
||||||
import Yesod.Default.Util (addStaticContentExternal)
|
import Yesod.Default.Util (addStaticContentExternal)
|
||||||
|
import qualified Yesod.Core.Unsafe as Unsafe
|
||||||
|
|
||||||
-- | The foundation datatype for your application. This can be a good place to
|
-- | The foundation datatype for your application. This can be a good place to
|
||||||
-- keep settings and values requiring initialization before your application
|
-- keep settings and values requiring initialization before your application
|
||||||
@ -291,6 +330,9 @@ instance YesodAuthPersist App
|
|||||||
instance RenderMessage App FormMessage where
|
instance RenderMessage App FormMessage where
|
||||||
renderMessage _ _ = defaultFormMessage
|
renderMessage _ _ = defaultFormMessage
|
||||||
|
|
||||||
|
unsafeHandler :: App -> Handler a -> IO a
|
||||||
|
unsafeHandler = Unsafe.fakeHandlerGetLogger appLogger
|
||||||
|
|
||||||
-- Note: Some functionality previously present in the scaffolding has been
|
-- Note: Some functionality previously present in the scaffolding has been
|
||||||
-- moved to documentation in the Wiki. Following are some hopefully helpful
|
-- moved to documentation in the Wiki. Following are some hopefully helpful
|
||||||
-- links:
|
-- links:
|
||||||
@ -411,6 +453,7 @@ Flag library-only
|
|||||||
Default: False
|
Default: False
|
||||||
|
|
||||||
library
|
library
|
||||||
|
hs-source-dirs: ., app
|
||||||
exposed-modules: Application
|
exposed-modules: Application
|
||||||
Foundation
|
Foundation
|
||||||
Import
|
Import
|
||||||
@ -446,7 +489,7 @@ library
|
|||||||
|
|
||||||
build-depends: base >= 4 && < 5
|
build-depends: base >= 4 && < 5
|
||||||
, yesod >= 1.4.1 && < 1.5
|
, yesod >= 1.4.1 && < 1.5
|
||||||
, yesod-core >= 1.4.0 && < 1.5
|
, yesod-core >= 1.4.6 && < 1.5
|
||||||
, yesod-auth >= 1.4.0 && < 1.5
|
, yesod-auth >= 1.4.0 && < 1.5
|
||||||
, yesod-static >= 1.4.0.3 && < 1.5
|
, yesod-static >= 1.4.0.3 && < 1.5
|
||||||
, yesod-form >= 1.4.0 && < 1.5
|
, yesod-form >= 1.4.0 && < 1.5
|
||||||
@ -683,40 +726,51 @@ import Yesod.Static (staticFiles)
|
|||||||
staticFiles (appStaticDir compileTimeAppSettings)
|
staticFiles (appStaticDir compileTimeAppSettings)
|
||||||
|
|
||||||
{-# START_FILE app/DevelMain.hs #-}
|
{-# START_FILE app/DevelMain.hs #-}
|
||||||
-- | Development version to be run inside GHCi.
|
-- | Running your app inside GHCi.
|
||||||
--
|
--
|
||||||
-- start this up with:
|
-- To start up GHCi for usage with Yesod, first make sure you are in dev mode:
|
||||||
--
|
--
|
||||||
-- cabal repl --ghc-options="-O0 -fobject-code"
|
-- > cabal configure -fdev
|
||||||
--
|
--
|
||||||
-- run with:
|
-- Note that @yesod devel@ automatically sets the dev flag.
|
||||||
|
-- Now launch the repl:
|
||||||
--
|
--
|
||||||
-- :l DevelMain
|
-- > cabal repl --ghc-options="-O0 -fobject-code"
|
||||||
-- DevelMain.update
|
|
||||||
--
|
--
|
||||||
-- You will need to add these packages to your .cabal file
|
-- To start your app, run:
|
||||||
-- * foreign-store >= 0.1 (very light-weight)
|
--
|
||||||
-- * warp (you already depend on this, it just isn't in your .cabal file)
|
-- > :l DevelMain
|
||||||
|
-- > DevelMain.update
|
||||||
|
--
|
||||||
|
-- You can also call @DevelMain.shutdown@ to stop the app
|
||||||
|
--
|
||||||
|
-- You will need to add the foreign-store package to your .cabal file.
|
||||||
|
-- It is very light-weight.
|
||||||
--
|
--
|
||||||
-- If you don't use cabal repl, you will need
|
-- If you don't use cabal repl, you will need
|
||||||
-- to add settings to your .ghci file.
|
-- to run the following in GHCi or to add it to
|
||||||
|
-- your .ghci file.
|
||||||
--
|
--
|
||||||
-- :set -DDEVELOPMENT
|
-- :set -DDEVELOPMENT
|
||||||
--
|
--
|
||||||
-- There is more information about using ghci
|
-- There is more information about this approach,
|
||||||
-- on the wiki: https://github.com/yesodweb/yesod/wiki/ghci
|
-- on the wiki: https://github.com/yesodweb/yesod/wiki/ghci
|
||||||
|
|
||||||
module DevelMain where
|
module DevelMain where
|
||||||
|
|
||||||
import Application (getApplicationDev)
|
import Prelude
|
||||||
|
import Application (getApplicationRepl, shutdownApp)
|
||||||
|
|
||||||
import Control.Exception (finally)
|
import Control.Exception (finally)
|
||||||
|
import Control.Monad ((>=>))
|
||||||
import Control.Concurrent
|
import Control.Concurrent
|
||||||
import Data.IORef
|
import Data.IORef
|
||||||
import Foreign.Store
|
import Foreign.Store
|
||||||
import Network.Wai.Handler.Warp
|
import Network.Wai.Handler.Warp
|
||||||
|
import GHC.Word
|
||||||
|
|
||||||
-- | Start or restart the server.
|
-- | Start or restart the server.
|
||||||
|
-- newStore is from foreign-store.
|
||||||
-- A Store holds onto some data across ghci reloads
|
-- A Store holds onto some data across ghci reloads
|
||||||
update :: IO ()
|
update :: IO ()
|
||||||
update = do
|
update = do
|
||||||
@ -729,27 +783,48 @@ update = do
|
|||||||
_ <- storeAction (Store tidStoreNum) (newIORef tid)
|
_ <- storeAction (Store tidStoreNum) (newIORef tid)
|
||||||
return ()
|
return ()
|
||||||
-- server is already running
|
-- server is already running
|
||||||
Just tidStore ->
|
Just tidStore -> restartAppInNewThread tidStore
|
||||||
-- shut the server down with killThread and wait for the done signal
|
|
||||||
modifyStoredIORef tidStore $ \tid -> do
|
|
||||||
killThread tid
|
|
||||||
withStore doneStore takeMVar >> readStore doneStore >>= start
|
|
||||||
where
|
where
|
||||||
|
doneStore :: Store (MVar ())
|
||||||
doneStore = Store 0
|
doneStore = Store 0
|
||||||
tidStoreNum = 1
|
|
||||||
|
|
||||||
modifyStoredIORef :: Store (IORef a) -> (a -> IO a) -> IO ()
|
-- shut the server down with killThread and wait for the done signal
|
||||||
modifyStoredIORef store f = withStore store $ \ref -> do
|
restartAppInNewThread :: Store (IORef ThreadId) -> IO ()
|
||||||
v <- readIORef ref
|
restartAppInNewThread tidStore = modifyStoredIORef tidStore $ \tid -> do
|
||||||
f v >>= writeIORef ref
|
killThread tid
|
||||||
|
withStore doneStore takeMVar
|
||||||
|
readStore doneStore >>= start
|
||||||
|
|
||||||
-- | Start the server in a separate thread.
|
|
||||||
start :: MVar () -- ^ Written to when the thread is killed.
|
-- | Start the server in a separate thread.
|
||||||
-> IO ThreadId
|
start :: MVar () -- ^ Written to when the thread is killed.
|
||||||
start done = do
|
-> IO ThreadId
|
||||||
(settings,app) <- getApplicationDev
|
start done = do
|
||||||
forkIO (finally (runSettings settings app)
|
(port, site, app) <- getApplicationRepl
|
||||||
(putMVar done ()))
|
forkIO (finally (runSettings (setPort port defaultSettings) app)
|
||||||
|
-- Note that this implies concurrency
|
||||||
|
-- between shutdownApp and the next app that is starting.
|
||||||
|
-- Normally this should be fine
|
||||||
|
(putMVar done () >> shutdownApp site))
|
||||||
|
|
||||||
|
-- | kill the server
|
||||||
|
shutdown :: IO ()
|
||||||
|
shutdown = do
|
||||||
|
mtidStore <- lookupStore tidStoreNum
|
||||||
|
case mtidStore of
|
||||||
|
-- no server running
|
||||||
|
Nothing -> putStrLn "no Yesod app running"
|
||||||
|
Just tidStore -> do
|
||||||
|
withStore tidStore $ readIORef >=> killThread
|
||||||
|
putStrLn "Yesod app is shutdown"
|
||||||
|
|
||||||
|
tidStoreNum :: Word32
|
||||||
|
tidStoreNum = 1
|
||||||
|
|
||||||
|
modifyStoredIORef :: Store (IORef a) -> (a -> IO a) -> IO ()
|
||||||
|
modifyStoredIORef store f = withStore store $ \ref -> do
|
||||||
|
v <- readIORef ref
|
||||||
|
f v >>= writeIORef ref
|
||||||
|
|
||||||
{-# START_FILE app/devel.hs #-}
|
{-# START_FILE app/devel.hs #-}
|
||||||
{-# LANGUAGE PackageImports #-}
|
{-# LANGUAGE PackageImports #-}
|
||||||
|
|||||||
@ -32,6 +32,12 @@ module Application
|
|||||||
, appMain
|
, appMain
|
||||||
, develMain
|
, develMain
|
||||||
, makeFoundation
|
, makeFoundation
|
||||||
|
-- * for DevelMain
|
||||||
|
, getApplicationRepl
|
||||||
|
, shutdownApp
|
||||||
|
-- * for GHCI
|
||||||
|
, handler
|
||||||
|
, db
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Monad.Logger (liftLoc, runLoggingT)
|
import Control.Monad.Logger (liftLoc, runLoggingT)
|
||||||
@ -42,7 +48,7 @@ import Language.Haskell.TH.Syntax (qLocation)
|
|||||||
import Network.Wai.Handler.Warp (Settings, defaultSettings,
|
import Network.Wai.Handler.Warp (Settings, defaultSettings,
|
||||||
defaultShouldDisplayException,
|
defaultShouldDisplayException,
|
||||||
runSettings, setHost,
|
runSettings, setHost,
|
||||||
setOnException, setPort)
|
setOnException, setPort, getPort)
|
||||||
import Network.Wai.Middleware.RequestLogger (Destination (Logger),
|
import Network.Wai.Middleware.RequestLogger (Destination (Logger),
|
||||||
IPAddrSource (..),
|
IPAddrSource (..),
|
||||||
OutputFormat (..), destination,
|
OutputFormat (..), destination,
|
||||||
@ -131,12 +137,15 @@ warpSettings foundation =
|
|||||||
-- | For yesod devel, return the Warp settings and WAI Application.
|
-- | For yesod devel, return the Warp settings and WAI Application.
|
||||||
getApplicationDev :: IO (Settings, Application)
|
getApplicationDev :: IO (Settings, Application)
|
||||||
getApplicationDev = do
|
getApplicationDev = do
|
||||||
settings <- loadAppSettings [configSettingsYml] [] useEnv
|
settings <- getAppSettings
|
||||||
foundation <- makeFoundation settings
|
foundation <- makeFoundation settings
|
||||||
app <- makeApplication foundation
|
|
||||||
wsettings <- getDevSettings $ warpSettings foundation
|
wsettings <- getDevSettings $ warpSettings foundation
|
||||||
|
app <- makeApplication foundation
|
||||||
return (wsettings, app)
|
return (wsettings, app)
|
||||||
|
|
||||||
|
getAppSettings :: IO AppSettings
|
||||||
|
getAppSettings = loadAppSettings [configSettingsYml] [] useEnv
|
||||||
|
|
||||||
-- | main function for use by yesod devel
|
-- | main function for use by yesod devel
|
||||||
develMain :: IO ()
|
develMain :: IO ()
|
||||||
develMain = develMainHelper getApplicationDev
|
develMain = develMainHelper getApplicationDev
|
||||||
@ -161,6 +170,34 @@ appMain = do
|
|||||||
-- Run the application with Warp
|
-- Run the application with Warp
|
||||||
runSettings (warpSettings foundation) app
|
runSettings (warpSettings foundation) app
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------------------------------------------
|
||||||
|
-- Functions for DevelMain.hs (a way to run the app from GHCi)
|
||||||
|
--------------------------------------------------------------
|
||||||
|
getApplicationRepl :: IO (Int, App, Application)
|
||||||
|
getApplicationRepl = do
|
||||||
|
settings <- getAppSettings
|
||||||
|
foundation <- makeFoundation settings
|
||||||
|
wsettings <- getDevSettings $ warpSettings foundation
|
||||||
|
app1 <- makeApplication foundation
|
||||||
|
return (getPort wsettings, foundation, app1)
|
||||||
|
|
||||||
|
shutdownApp :: App -> IO ()
|
||||||
|
shutdownApp _ = return ()
|
||||||
|
|
||||||
|
|
||||||
|
---------------------------------------------
|
||||||
|
-- Functions for use in development with GHCi
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
-- | Run a handler
|
||||||
|
handler :: Handler a -> IO a
|
||||||
|
handler h = getAppSettings >>= makeFoundation >>= flip unsafeHandler h
|
||||||
|
|
||||||
|
-- | Run DB queries
|
||||||
|
db :: ReaderT SqlBackend (HandlerT App IO) a -> IO a
|
||||||
|
db = handler . runDB
|
||||||
|
|
||||||
{-# START_FILE Foundation.hs #-}
|
{-# START_FILE Foundation.hs #-}
|
||||||
module Foundation where
|
module Foundation where
|
||||||
|
|
||||||
@ -171,6 +208,7 @@ import Text.Jasmine (minifym)
|
|||||||
import Yesod.Auth.BrowserId (authBrowserId)
|
import Yesod.Auth.BrowserId (authBrowserId)
|
||||||
import Yesod.Default.Util (addStaticContentExternal)
|
import Yesod.Default.Util (addStaticContentExternal)
|
||||||
import Yesod.Core.Types (Logger)
|
import Yesod.Core.Types (Logger)
|
||||||
|
import qualified Yesod.Core.Unsafe as Unsafe
|
||||||
|
|
||||||
-- | The foundation datatype for your application. This can be a good place to
|
-- | The foundation datatype for your application. This can be a good place to
|
||||||
-- keep settings and values requiring initialization before your application
|
-- keep settings and values requiring initialization before your application
|
||||||
@ -306,6 +344,9 @@ instance YesodAuthPersist App
|
|||||||
instance RenderMessage App FormMessage where
|
instance RenderMessage App FormMessage where
|
||||||
renderMessage _ _ = defaultFormMessage
|
renderMessage _ _ = defaultFormMessage
|
||||||
|
|
||||||
|
unsafeHandler :: App -> Handler a -> IO a
|
||||||
|
unsafeHandler = Unsafe.fakeHandlerGetLogger appLogger
|
||||||
|
|
||||||
-- Note: Some functionality previously present in the scaffolding has been
|
-- Note: Some functionality previously present in the scaffolding has been
|
||||||
-- moved to documentation in the Wiki. Following are some hopefully helpful
|
-- moved to documentation in the Wiki. Following are some hopefully helpful
|
||||||
-- links:
|
-- links:
|
||||||
@ -423,6 +464,7 @@ Flag library-only
|
|||||||
Default: False
|
Default: False
|
||||||
|
|
||||||
library
|
library
|
||||||
|
hs-source-dirs: ., app
|
||||||
exposed-modules: Application
|
exposed-modules: Application
|
||||||
Foundation
|
Foundation
|
||||||
Import
|
Import
|
||||||
@ -458,7 +500,7 @@ library
|
|||||||
|
|
||||||
build-depends: base >= 4 && < 5
|
build-depends: base >= 4 && < 5
|
||||||
, yesod >= 1.4.1 && < 1.5
|
, yesod >= 1.4.1 && < 1.5
|
||||||
, yesod-core >= 1.4.0 && < 1.5
|
, yesod-core >= 1.4.6 && < 1.5
|
||||||
, yesod-auth >= 1.4.0 && < 1.5
|
, yesod-auth >= 1.4.0 && < 1.5
|
||||||
, yesod-static >= 1.4.0.3 && < 1.5
|
, yesod-static >= 1.4.0.3 && < 1.5
|
||||||
, yesod-form >= 1.4.0 && < 1.5
|
, yesod-form >= 1.4.0 && < 1.5
|
||||||
@ -695,40 +737,51 @@ import Yesod.Static (staticFiles)
|
|||||||
staticFiles (appStaticDir compileTimeAppSettings)
|
staticFiles (appStaticDir compileTimeAppSettings)
|
||||||
|
|
||||||
{-# START_FILE app/DevelMain.hs #-}
|
{-# START_FILE app/DevelMain.hs #-}
|
||||||
-- | Development version to be run inside GHCi.
|
-- | Running your app inside GHCi.
|
||||||
--
|
--
|
||||||
-- start this up with:
|
-- To start up GHCi for usage with Yesod, first make sure you are in dev mode:
|
||||||
--
|
--
|
||||||
-- cabal repl --ghc-options="-O0 -fobject-code"
|
-- > cabal configure -fdev
|
||||||
--
|
--
|
||||||
-- run with:
|
-- Note that @yesod devel@ automatically sets the dev flag.
|
||||||
|
-- Now launch the repl:
|
||||||
--
|
--
|
||||||
-- :l DevelMain
|
-- > cabal repl --ghc-options="-O0 -fobject-code"
|
||||||
-- DevelMain.update
|
|
||||||
--
|
--
|
||||||
-- You will need to add these packages to your .cabal file
|
-- To start your app, run:
|
||||||
-- * foreign-store >= 0.1 (very light-weight)
|
--
|
||||||
-- * warp (you already depend on this, it just isn't in your .cabal file)
|
-- > :l DevelMain
|
||||||
|
-- > DevelMain.update
|
||||||
|
--
|
||||||
|
-- You can also call @DevelMain.shutdown@ to stop the app
|
||||||
|
--
|
||||||
|
-- You will need to add the foreign-store package to your .cabal file.
|
||||||
|
-- It is very light-weight.
|
||||||
--
|
--
|
||||||
-- If you don't use cabal repl, you will need
|
-- If you don't use cabal repl, you will need
|
||||||
-- to add settings to your .ghci file.
|
-- to run the following in GHCi or to add it to
|
||||||
|
-- your .ghci file.
|
||||||
--
|
--
|
||||||
-- :set -DDEVELOPMENT
|
-- :set -DDEVELOPMENT
|
||||||
--
|
--
|
||||||
-- There is more information about using ghci
|
-- There is more information about this approach,
|
||||||
-- on the wiki: https://github.com/yesodweb/yesod/wiki/ghci
|
-- on the wiki: https://github.com/yesodweb/yesod/wiki/ghci
|
||||||
|
|
||||||
module DevelMain where
|
module DevelMain where
|
||||||
|
|
||||||
import Application (getApplicationDev)
|
import Prelude
|
||||||
|
import Application (getApplicationRepl, shutdownApp)
|
||||||
|
|
||||||
import Control.Exception (finally)
|
import Control.Exception (finally)
|
||||||
|
import Control.Monad ((>=>))
|
||||||
import Control.Concurrent
|
import Control.Concurrent
|
||||||
import Data.IORef
|
import Data.IORef
|
||||||
import Foreign.Store
|
import Foreign.Store
|
||||||
import Network.Wai.Handler.Warp
|
import Network.Wai.Handler.Warp
|
||||||
|
import GHC.Word
|
||||||
|
|
||||||
-- | Start or restart the server.
|
-- | Start or restart the server.
|
||||||
|
-- newStore is from foreign-store.
|
||||||
-- A Store holds onto some data across ghci reloads
|
-- A Store holds onto some data across ghci reloads
|
||||||
update :: IO ()
|
update :: IO ()
|
||||||
update = do
|
update = do
|
||||||
@ -741,27 +794,48 @@ update = do
|
|||||||
_ <- storeAction (Store tidStoreNum) (newIORef tid)
|
_ <- storeAction (Store tidStoreNum) (newIORef tid)
|
||||||
return ()
|
return ()
|
||||||
-- server is already running
|
-- server is already running
|
||||||
Just tidStore ->
|
Just tidStore -> restartAppInNewThread tidStore
|
||||||
-- shut the server down with killThread and wait for the done signal
|
|
||||||
modifyStoredIORef tidStore $ \tid -> do
|
|
||||||
killThread tid
|
|
||||||
withStore doneStore takeMVar >> readStore doneStore >>= start
|
|
||||||
where
|
where
|
||||||
|
doneStore :: Store (MVar ())
|
||||||
doneStore = Store 0
|
doneStore = Store 0
|
||||||
tidStoreNum = 1
|
|
||||||
|
|
||||||
modifyStoredIORef :: Store (IORef a) -> (a -> IO a) -> IO ()
|
-- shut the server down with killThread and wait for the done signal
|
||||||
modifyStoredIORef store f = withStore store $ \ref -> do
|
restartAppInNewThread :: Store (IORef ThreadId) -> IO ()
|
||||||
v <- readIORef ref
|
restartAppInNewThread tidStore = modifyStoredIORef tidStore $ \tid -> do
|
||||||
f v >>= writeIORef ref
|
killThread tid
|
||||||
|
withStore doneStore takeMVar
|
||||||
|
readStore doneStore >>= start
|
||||||
|
|
||||||
-- | Start the server in a separate thread.
|
|
||||||
start :: MVar () -- ^ Written to when the thread is killed.
|
-- | Start the server in a separate thread.
|
||||||
-> IO ThreadId
|
start :: MVar () -- ^ Written to when the thread is killed.
|
||||||
start done = do
|
-> IO ThreadId
|
||||||
(settings,app) <- getApplicationDev
|
start done = do
|
||||||
forkIO (finally (runSettings settings app)
|
(port, site, app) <- getApplicationRepl
|
||||||
(putMVar done ()))
|
forkIO (finally (runSettings (setPort port defaultSettings) app)
|
||||||
|
-- Note that this implies concurrency
|
||||||
|
-- between shutdownApp and the next app that is starting.
|
||||||
|
-- Normally this should be fine
|
||||||
|
(putMVar done () >> shutdownApp site))
|
||||||
|
|
||||||
|
-- | kill the server
|
||||||
|
shutdown :: IO ()
|
||||||
|
shutdown = do
|
||||||
|
mtidStore <- lookupStore tidStoreNum
|
||||||
|
case mtidStore of
|
||||||
|
-- no server running
|
||||||
|
Nothing -> putStrLn "no Yesod app running"
|
||||||
|
Just tidStore -> do
|
||||||
|
withStore tidStore $ readIORef >=> killThread
|
||||||
|
putStrLn "Yesod app is shutdown"
|
||||||
|
|
||||||
|
tidStoreNum :: Word32
|
||||||
|
tidStoreNum = 1
|
||||||
|
|
||||||
|
modifyStoredIORef :: Store (IORef a) -> (a -> IO a) -> IO ()
|
||||||
|
modifyStoredIORef store f = withStore store $ \ref -> do
|
||||||
|
v <- readIORef ref
|
||||||
|
f v >>= writeIORef ref
|
||||||
|
|
||||||
{-# START_FILE app/devel.hs #-}
|
{-# START_FILE app/devel.hs #-}
|
||||||
{-# LANGUAGE PackageImports #-}
|
{-# LANGUAGE PackageImports #-}
|
||||||
@ -9032,7 +9106,7 @@ module TestImport
|
|||||||
import Application (makeFoundation)
|
import Application (makeFoundation)
|
||||||
import ClassyPrelude as X
|
import ClassyPrelude as X
|
||||||
import Database.Persist as X hiding (get)
|
import Database.Persist as X hiding (get)
|
||||||
import Database.Persist.Sql (SqlPersistM, runSqlPersistMPool)
|
import Database.Persist.Sql (SqlPersistM, SqlBackend, runSqlPersistMPool, rawExecute, rawSql, unSingle, connEscapeName)
|
||||||
import Foundation as X
|
import Foundation as X
|
||||||
import Model as X
|
import Model as X
|
||||||
import Test.Hspec as X
|
import Test.Hspec as X
|
||||||
@ -9041,8 +9115,11 @@ import Yesod.Test as X
|
|||||||
|
|
||||||
runDB :: SqlPersistM a -> YesodExample App a
|
runDB :: SqlPersistM a -> YesodExample App a
|
||||||
runDB query = do
|
runDB query = do
|
||||||
pool <- fmap appConnPool getTestYesod
|
app <- getTestYesod
|
||||||
liftIO $ runSqlPersistMPool query pool
|
liftIO $ runDBWithApp app query
|
||||||
|
|
||||||
|
runDBWithApp :: App -> SqlPersistM a -> IO a
|
||||||
|
runDBWithApp app query = runSqlPersistMPool query (appConnPool app)
|
||||||
|
|
||||||
withApp :: SpecWith App -> Spec
|
withApp :: SpecWith App -> Spec
|
||||||
withApp = before $ do
|
withApp = before $ do
|
||||||
@ -9050,5 +9127,30 @@ withApp = before $ do
|
|||||||
["config/test-settings.yml", "config/settings.yml"]
|
["config/test-settings.yml", "config/settings.yml"]
|
||||||
[]
|
[]
|
||||||
ignoreEnv
|
ignoreEnv
|
||||||
makeFoundation settings
|
foundation <- makeFoundation settings
|
||||||
|
wipeDB foundation
|
||||||
|
return foundation
|
||||||
|
|
||||||
|
-- This function will truncate all of the tables in your database.
|
||||||
|
-- 'withApp' calls it before each test, creating a clean environment for each
|
||||||
|
-- spec to run in.
|
||||||
|
wipeDB :: App -> IO ()
|
||||||
|
wipeDB app = do
|
||||||
|
runDBWithApp app $ do
|
||||||
|
tables <- getTables
|
||||||
|
sqlBackend <- ask
|
||||||
|
let queries = map (\t -> "TRUNCATE TABLE " ++ (connEscapeName sqlBackend $ DBName t)) tables
|
||||||
|
|
||||||
|
-- In MySQL, a table cannot be truncated if another table references it via foreign key.
|
||||||
|
-- Since we're wiping both the parent and child tables, though, it's safe
|
||||||
|
-- to temporarily disable this check.
|
||||||
|
rawExecute "SET foreign_key_checks = 0;" []
|
||||||
|
forM_ queries (\q -> rawExecute q [])
|
||||||
|
rawExecute "SET foreign_key_checks = 1;" []
|
||||||
|
return ()
|
||||||
|
|
||||||
|
getTables :: MonadIO m => ReaderT SqlBackend m [Text]
|
||||||
|
getTables = do
|
||||||
|
tables <- rawSql "SHOW TABLES;" []
|
||||||
|
return $ map unSingle tables
|
||||||
|
|
||||||
|
|||||||
@ -35,6 +35,12 @@ module Application
|
|||||||
, appMain
|
, appMain
|
||||||
, develMain
|
, develMain
|
||||||
, makeFoundation
|
, makeFoundation
|
||||||
|
-- * for DevelMain
|
||||||
|
, getApplicationRepl
|
||||||
|
, shutdownApp
|
||||||
|
-- * for GHCI
|
||||||
|
, handler
|
||||||
|
, db
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Monad.Logger (liftLoc, runLoggingT)
|
import Control.Monad.Logger (liftLoc, runLoggingT)
|
||||||
@ -45,7 +51,7 @@ import Language.Haskell.TH.Syntax (qLocation)
|
|||||||
import Network.Wai.Handler.Warp (Settings, defaultSettings,
|
import Network.Wai.Handler.Warp (Settings, defaultSettings,
|
||||||
defaultShouldDisplayException,
|
defaultShouldDisplayException,
|
||||||
runSettings, setHost,
|
runSettings, setHost,
|
||||||
setOnException, setPort)
|
setOnException, setPort, getPort)
|
||||||
import Network.Wai.Middleware.RequestLogger (Destination (Logger),
|
import Network.Wai.Middleware.RequestLogger (Destination (Logger),
|
||||||
IPAddrSource (..),
|
IPAddrSource (..),
|
||||||
OutputFormat (..), destination,
|
OutputFormat (..), destination,
|
||||||
@ -137,12 +143,15 @@ warpSettings foundation =
|
|||||||
-- | For yesod devel, return the Warp settings and WAI Application.
|
-- | For yesod devel, return the Warp settings and WAI Application.
|
||||||
getApplicationDev :: IO (Settings, Application)
|
getApplicationDev :: IO (Settings, Application)
|
||||||
getApplicationDev = do
|
getApplicationDev = do
|
||||||
settings <- loadAppSettings [configSettingsYml] [] useEnv
|
settings <- getAppSettings
|
||||||
foundation <- makeFoundation settings
|
foundation <- makeFoundation settings
|
||||||
app <- makeApplication foundation
|
|
||||||
wsettings <- getDevSettings $ warpSettings foundation
|
wsettings <- getDevSettings $ warpSettings foundation
|
||||||
|
app <- makeApplication foundation
|
||||||
return (wsettings, app)
|
return (wsettings, app)
|
||||||
|
|
||||||
|
getAppSettings :: IO AppSettings
|
||||||
|
getAppSettings = loadAppSettings [configSettingsYml] [] useEnv
|
||||||
|
|
||||||
-- | main function for use by yesod devel
|
-- | main function for use by yesod devel
|
||||||
develMain :: IO ()
|
develMain :: IO ()
|
||||||
develMain = develMainHelper getApplicationDev
|
develMain = develMainHelper getApplicationDev
|
||||||
@ -167,15 +176,44 @@ appMain = do
|
|||||||
-- Run the application with Warp
|
-- Run the application with Warp
|
||||||
runSettings (warpSettings foundation) app
|
runSettings (warpSettings foundation) app
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------------------------------------------
|
||||||
|
-- Functions for DevelMain.hs (a way to run the app from GHCi)
|
||||||
|
--------------------------------------------------------------
|
||||||
|
getApplicationRepl :: IO (Int, App, Application)
|
||||||
|
getApplicationRepl = do
|
||||||
|
settings <- getAppSettings
|
||||||
|
foundation <- makeFoundation settings
|
||||||
|
wsettings <- getDevSettings $ warpSettings foundation
|
||||||
|
app1 <- makeApplication foundation
|
||||||
|
return (getPort wsettings, foundation, app1)
|
||||||
|
|
||||||
|
shutdownApp :: App -> IO ()
|
||||||
|
shutdownApp _ = return ()
|
||||||
|
|
||||||
|
|
||||||
|
---------------------------------------------
|
||||||
|
-- Functions for use in development with GHCi
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
-- | Run a handler
|
||||||
|
handler :: Handler a -> IO a
|
||||||
|
handler h = getAppSettings >>= makeFoundation >>= flip unsafeHandler h
|
||||||
|
|
||||||
|
-- | Run DB queries
|
||||||
|
db :: ReaderT SqlBackend (HandlerT App IO) a -> IO a
|
||||||
|
db = handler . runDB
|
||||||
|
|
||||||
{-# START_FILE Foundation.hs #-}
|
{-# START_FILE Foundation.hs #-}
|
||||||
module Foundation where
|
module Foundation where
|
||||||
|
|
||||||
import Database.Persist.Sql (ConnectionPool, runSqlPool)
|
import Database.Persist.Sql (ConnectionPool, runSqlPool)
|
||||||
import Import.NoFoundation
|
import Import.NoFoundation
|
||||||
import Text.Hamlet (hamletFile)
|
import Text.Hamlet (hamletFile)
|
||||||
import Yesod.Auth.BrowserId (authBrowserId)
|
import Yesod.Auth.BrowserId (authBrowserId)
|
||||||
import Yesod.Core.Types (Logger)
|
import qualified Yesod.Core.Unsafe as Unsafe
|
||||||
import Yesod.Default.Util (addStaticContentExternal)
|
import Yesod.Core.Types (Logger)
|
||||||
|
import Yesod.Default.Util (addStaticContentExternal)
|
||||||
import Yesod.Fay
|
import Yesod.Fay
|
||||||
|
|
||||||
-- | The foundation datatype for your application. This can be a good place to
|
-- | The foundation datatype for your application. This can be a good place to
|
||||||
@ -322,6 +360,9 @@ instance YesodAuthPersist App
|
|||||||
instance RenderMessage App FormMessage where
|
instance RenderMessage App FormMessage where
|
||||||
renderMessage _ _ = defaultFormMessage
|
renderMessage _ _ = defaultFormMessage
|
||||||
|
|
||||||
|
unsafeHandler :: App -> Handler a -> IO a
|
||||||
|
unsafeHandler = Unsafe.fakeHandlerGetLogger appLogger
|
||||||
|
|
||||||
-- Note: Some functionality previously present in the scaffolding has been
|
-- Note: Some functionality previously present in the scaffolding has been
|
||||||
-- moved to documentation in the Wiki. Following are some hopefully helpful
|
-- moved to documentation in the Wiki. Following are some hopefully helpful
|
||||||
-- links:
|
-- links:
|
||||||
@ -463,7 +504,7 @@ Flag library-only
|
|||||||
Default: False
|
Default: False
|
||||||
|
|
||||||
library
|
library
|
||||||
hs-source-dirs: ., fay-shared
|
hs-source-dirs: ., fay-shared, app
|
||||||
exposed-modules: Application
|
exposed-modules: Application
|
||||||
Foundation
|
Foundation
|
||||||
Import
|
Import
|
||||||
@ -502,7 +543,7 @@ library
|
|||||||
|
|
||||||
build-depends: base >= 4 && < 5
|
build-depends: base >= 4 && < 5
|
||||||
, yesod >= 1.4.1 && < 1.5
|
, yesod >= 1.4.1 && < 1.5
|
||||||
, yesod-core >= 1.4.0 && < 1.5
|
, yesod-core >= 1.4.6 && < 1.5
|
||||||
, yesod-auth >= 1.4.0 && < 1.5
|
, yesod-auth >= 1.4.0 && < 1.5
|
||||||
, yesod-static >= 1.4.0.3 && < 1.5
|
, yesod-static >= 1.4.0.3 && < 1.5
|
||||||
, yesod-form >= 1.4.0 && < 1.5
|
, yesod-form >= 1.4.0 && < 1.5
|
||||||
@ -755,40 +796,51 @@ import Yesod.Static (staticFiles)
|
|||||||
staticFiles (appStaticDir compileTimeAppSettings)
|
staticFiles (appStaticDir compileTimeAppSettings)
|
||||||
|
|
||||||
{-# START_FILE app/DevelMain.hs #-}
|
{-# START_FILE app/DevelMain.hs #-}
|
||||||
-- | Development version to be run inside GHCi.
|
-- | Running your app inside GHCi.
|
||||||
--
|
--
|
||||||
-- start this up with:
|
-- To start up GHCi for usage with Yesod, first make sure you are in dev mode:
|
||||||
--
|
--
|
||||||
-- cabal repl --ghc-options="-O0 -fobject-code"
|
-- > cabal configure -fdev
|
||||||
--
|
--
|
||||||
-- run with:
|
-- Note that @yesod devel@ automatically sets the dev flag.
|
||||||
|
-- Now launch the repl:
|
||||||
--
|
--
|
||||||
-- :l DevelMain
|
-- > cabal repl --ghc-options="-O0 -fobject-code"
|
||||||
-- DevelMain.update
|
|
||||||
--
|
--
|
||||||
-- You will need to add these packages to your .cabal file
|
-- To start your app, run:
|
||||||
-- * foreign-store >= 0.1 (very light-weight)
|
--
|
||||||
-- * warp (you already depend on this, it just isn't in your .cabal file)
|
-- > :l DevelMain
|
||||||
|
-- > DevelMain.update
|
||||||
|
--
|
||||||
|
-- You can also call @DevelMain.shutdown@ to stop the app
|
||||||
|
--
|
||||||
|
-- You will need to add the foreign-store package to your .cabal file.
|
||||||
|
-- It is very light-weight.
|
||||||
--
|
--
|
||||||
-- If you don't use cabal repl, you will need
|
-- If you don't use cabal repl, you will need
|
||||||
-- to add settings to your .ghci file.
|
-- to run the following in GHCi or to add it to
|
||||||
|
-- your .ghci file.
|
||||||
--
|
--
|
||||||
-- :set -DDEVELOPMENT
|
-- :set -DDEVELOPMENT
|
||||||
--
|
--
|
||||||
-- There is more information about using ghci
|
-- There is more information about this approach,
|
||||||
-- on the wiki: https://github.com/yesodweb/yesod/wiki/ghci
|
-- on the wiki: https://github.com/yesodweb/yesod/wiki/ghci
|
||||||
|
|
||||||
module DevelMain where
|
module DevelMain where
|
||||||
|
|
||||||
import Application (getApplicationDev)
|
import Prelude
|
||||||
|
import Application (getApplicationRepl, shutdownApp)
|
||||||
|
|
||||||
import Control.Exception (finally)
|
import Control.Exception (finally)
|
||||||
|
import Control.Monad ((>=>))
|
||||||
import Control.Concurrent
|
import Control.Concurrent
|
||||||
import Data.IORef
|
import Data.IORef
|
||||||
import Foreign.Store
|
import Foreign.Store
|
||||||
import Network.Wai.Handler.Warp
|
import Network.Wai.Handler.Warp
|
||||||
|
import GHC.Word
|
||||||
|
|
||||||
-- | Start or restart the server.
|
-- | Start or restart the server.
|
||||||
|
-- newStore is from foreign-store.
|
||||||
-- A Store holds onto some data across ghci reloads
|
-- A Store holds onto some data across ghci reloads
|
||||||
update :: IO ()
|
update :: IO ()
|
||||||
update = do
|
update = do
|
||||||
@ -801,27 +853,48 @@ update = do
|
|||||||
_ <- storeAction (Store tidStoreNum) (newIORef tid)
|
_ <- storeAction (Store tidStoreNum) (newIORef tid)
|
||||||
return ()
|
return ()
|
||||||
-- server is already running
|
-- server is already running
|
||||||
Just tidStore ->
|
Just tidStore -> restartAppInNewThread tidStore
|
||||||
-- shut the server down with killThread and wait for the done signal
|
|
||||||
modifyStoredIORef tidStore $ \tid -> do
|
|
||||||
killThread tid
|
|
||||||
withStore doneStore takeMVar >> readStore doneStore >>= start
|
|
||||||
where
|
where
|
||||||
|
doneStore :: Store (MVar ())
|
||||||
doneStore = Store 0
|
doneStore = Store 0
|
||||||
tidStoreNum = 1
|
|
||||||
|
|
||||||
modifyStoredIORef :: Store (IORef a) -> (a -> IO a) -> IO ()
|
-- shut the server down with killThread and wait for the done signal
|
||||||
modifyStoredIORef store f = withStore store $ \ref -> do
|
restartAppInNewThread :: Store (IORef ThreadId) -> IO ()
|
||||||
v <- readIORef ref
|
restartAppInNewThread tidStore = modifyStoredIORef tidStore $ \tid -> do
|
||||||
f v >>= writeIORef ref
|
killThread tid
|
||||||
|
withStore doneStore takeMVar
|
||||||
|
readStore doneStore >>= start
|
||||||
|
|
||||||
-- | Start the server in a separate thread.
|
|
||||||
start :: MVar () -- ^ Written to when the thread is killed.
|
-- | Start the server in a separate thread.
|
||||||
-> IO ThreadId
|
start :: MVar () -- ^ Written to when the thread is killed.
|
||||||
start done = do
|
-> IO ThreadId
|
||||||
(settings,app) <- getApplicationDev
|
start done = do
|
||||||
forkIO (finally (runSettings settings app)
|
(port, site, app) <- getApplicationRepl
|
||||||
(putMVar done ()))
|
forkIO (finally (runSettings (setPort port defaultSettings) app)
|
||||||
|
-- Note that this implies concurrency
|
||||||
|
-- between shutdownApp and the next app that is starting.
|
||||||
|
-- Normally this should be fine
|
||||||
|
(putMVar done () >> shutdownApp site))
|
||||||
|
|
||||||
|
-- | kill the server
|
||||||
|
shutdown :: IO ()
|
||||||
|
shutdown = do
|
||||||
|
mtidStore <- lookupStore tidStoreNum
|
||||||
|
case mtidStore of
|
||||||
|
-- no server running
|
||||||
|
Nothing -> putStrLn "no Yesod app running"
|
||||||
|
Just tidStore -> do
|
||||||
|
withStore tidStore $ readIORef >=> killThread
|
||||||
|
putStrLn "Yesod app is shutdown"
|
||||||
|
|
||||||
|
tidStoreNum :: Word32
|
||||||
|
tidStoreNum = 1
|
||||||
|
|
||||||
|
modifyStoredIORef :: Store (IORef a) -> (a -> IO a) -> IO ()
|
||||||
|
modifyStoredIORef store f = withStore store $ \ref -> do
|
||||||
|
v <- readIORef ref
|
||||||
|
f v >>= writeIORef ref
|
||||||
|
|
||||||
{-# START_FILE app/devel.hs #-}
|
{-# START_FILE app/devel.hs #-}
|
||||||
{-# LANGUAGE PackageImports #-}
|
{-# LANGUAGE PackageImports #-}
|
||||||
@ -9153,7 +9226,7 @@ module TestImport
|
|||||||
import Application (makeFoundation)
|
import Application (makeFoundation)
|
||||||
import ClassyPrelude as X
|
import ClassyPrelude as X
|
||||||
import Database.Persist as X hiding (get)
|
import Database.Persist as X hiding (get)
|
||||||
import Database.Persist.Sql (SqlPersistM, runSqlPersistMPool)
|
import Database.Persist.Sql (SqlPersistM, SqlBackend, runSqlPersistMPool, rawExecute, rawSql, unSingle, connEscapeName)
|
||||||
import Foundation as X
|
import Foundation as X
|
||||||
import Model as X
|
import Model as X
|
||||||
import Test.Hspec as X
|
import Test.Hspec as X
|
||||||
@ -9162,8 +9235,12 @@ import Yesod.Test as X
|
|||||||
|
|
||||||
runDB :: SqlPersistM a -> YesodExample App a
|
runDB :: SqlPersistM a -> YesodExample App a
|
||||||
runDB query = do
|
runDB query = do
|
||||||
pool <- fmap appConnPool getTestYesod
|
app <- getTestYesod
|
||||||
liftIO $ runSqlPersistMPool query pool
|
liftIO $ runDBWithApp app query
|
||||||
|
|
||||||
|
runDBWithApp :: App -> SqlPersistM a -> IO a
|
||||||
|
runDBWithApp app query = runSqlPersistMPool query (appConnPool app)
|
||||||
|
|
||||||
|
|
||||||
withApp :: SpecWith App -> Spec
|
withApp :: SpecWith App -> Spec
|
||||||
withApp = before $ do
|
withApp = before $ do
|
||||||
@ -9171,5 +9248,24 @@ withApp = before $ do
|
|||||||
["config/test-settings.yml", "config/settings.yml"]
|
["config/test-settings.yml", "config/settings.yml"]
|
||||||
[]
|
[]
|
||||||
ignoreEnv
|
ignoreEnv
|
||||||
makeFoundation settings
|
foundation <- makeFoundation settings
|
||||||
|
wipeDB foundation
|
||||||
|
return foundation
|
||||||
|
|
||||||
|
-- This function will truncate all of the tables in your database.
|
||||||
|
-- 'withApp' calls it before each test, creating a clean environment for each
|
||||||
|
-- spec to run in.
|
||||||
|
wipeDB :: App -> IO ()
|
||||||
|
wipeDB app = do
|
||||||
|
runDBWithApp app $ do
|
||||||
|
tables <- getTables
|
||||||
|
sqlBackend <- ask
|
||||||
|
|
||||||
|
let escapedTables = map (connEscapeName sqlBackend . DBName) tables
|
||||||
|
query = "TRUNCATE TABLE " ++ (intercalate ", " escapedTables)
|
||||||
|
rawExecute query []
|
||||||
|
|
||||||
|
getTables :: MonadIO m => ReaderT SqlBackend m [Text]
|
||||||
|
getTables = do
|
||||||
|
tables <- rawSql "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';" []
|
||||||
|
return $ map unSingle tables
|
||||||
|
|||||||
@ -32,6 +32,12 @@ module Application
|
|||||||
, appMain
|
, appMain
|
||||||
, develMain
|
, develMain
|
||||||
, makeFoundation
|
, makeFoundation
|
||||||
|
-- * for DevelMain
|
||||||
|
, getApplicationRepl
|
||||||
|
, shutdownApp
|
||||||
|
-- * for GHCI
|
||||||
|
, handler
|
||||||
|
, db
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Monad.Logger (liftLoc, runLoggingT)
|
import Control.Monad.Logger (liftLoc, runLoggingT)
|
||||||
@ -42,7 +48,7 @@ import Language.Haskell.TH.Syntax (qLocation)
|
|||||||
import Network.Wai.Handler.Warp (Settings, defaultSettings,
|
import Network.Wai.Handler.Warp (Settings, defaultSettings,
|
||||||
defaultShouldDisplayException,
|
defaultShouldDisplayException,
|
||||||
runSettings, setHost,
|
runSettings, setHost,
|
||||||
setOnException, setPort)
|
setOnException, setPort, getPort)
|
||||||
import Network.Wai.Middleware.RequestLogger (Destination (Logger),
|
import Network.Wai.Middleware.RequestLogger (Destination (Logger),
|
||||||
IPAddrSource (..),
|
IPAddrSource (..),
|
||||||
OutputFormat (..), destination,
|
OutputFormat (..), destination,
|
||||||
@ -131,12 +137,15 @@ warpSettings foundation =
|
|||||||
-- | For yesod devel, return the Warp settings and WAI Application.
|
-- | For yesod devel, return the Warp settings and WAI Application.
|
||||||
getApplicationDev :: IO (Settings, Application)
|
getApplicationDev :: IO (Settings, Application)
|
||||||
getApplicationDev = do
|
getApplicationDev = do
|
||||||
settings <- loadAppSettings [configSettingsYml] [] useEnv
|
settings <- getAppSettings
|
||||||
foundation <- makeFoundation settings
|
foundation <- makeFoundation settings
|
||||||
app <- makeApplication foundation
|
|
||||||
wsettings <- getDevSettings $ warpSettings foundation
|
wsettings <- getDevSettings $ warpSettings foundation
|
||||||
|
app <- makeApplication foundation
|
||||||
return (wsettings, app)
|
return (wsettings, app)
|
||||||
|
|
||||||
|
getAppSettings :: IO AppSettings
|
||||||
|
getAppSettings = loadAppSettings [configSettingsYml] [] useEnv
|
||||||
|
|
||||||
-- | main function for use by yesod devel
|
-- | main function for use by yesod devel
|
||||||
develMain :: IO ()
|
develMain :: IO ()
|
||||||
develMain = develMainHelper getApplicationDev
|
develMain = develMainHelper getApplicationDev
|
||||||
@ -161,6 +170,34 @@ appMain = do
|
|||||||
-- Run the application with Warp
|
-- Run the application with Warp
|
||||||
runSettings (warpSettings foundation) app
|
runSettings (warpSettings foundation) app
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------------------------------------------
|
||||||
|
-- Functions for DevelMain.hs (a way to run the app from GHCi)
|
||||||
|
--------------------------------------------------------------
|
||||||
|
getApplicationRepl :: IO (Int, App, Application)
|
||||||
|
getApplicationRepl = do
|
||||||
|
settings <- getAppSettings
|
||||||
|
foundation <- makeFoundation settings
|
||||||
|
wsettings <- getDevSettings $ warpSettings foundation
|
||||||
|
app1 <- makeApplication foundation
|
||||||
|
return (getPort wsettings, foundation, app1)
|
||||||
|
|
||||||
|
shutdownApp :: App -> IO ()
|
||||||
|
shutdownApp _ = return ()
|
||||||
|
|
||||||
|
|
||||||
|
---------------------------------------------
|
||||||
|
-- Functions for use in development with GHCi
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
-- | Run a handler
|
||||||
|
handler :: Handler a -> IO a
|
||||||
|
handler h = getAppSettings >>= makeFoundation >>= flip unsafeHandler h
|
||||||
|
|
||||||
|
-- | Run DB queries
|
||||||
|
db :: ReaderT SqlBackend (HandlerT App IO) a -> IO a
|
||||||
|
db = handler . runDB
|
||||||
|
|
||||||
{-# START_FILE Foundation.hs #-}
|
{-# START_FILE Foundation.hs #-}
|
||||||
module Foundation where
|
module Foundation where
|
||||||
|
|
||||||
@ -171,6 +208,7 @@ import Text.Jasmine (minifym)
|
|||||||
import Yesod.Auth.BrowserId (authBrowserId)
|
import Yesod.Auth.BrowserId (authBrowserId)
|
||||||
import Yesod.Default.Util (addStaticContentExternal)
|
import Yesod.Default.Util (addStaticContentExternal)
|
||||||
import Yesod.Core.Types (Logger)
|
import Yesod.Core.Types (Logger)
|
||||||
|
import qualified Yesod.Core.Unsafe as Unsafe
|
||||||
|
|
||||||
-- | The foundation datatype for your application. This can be a good place to
|
-- | The foundation datatype for your application. This can be a good place to
|
||||||
-- keep settings and values requiring initialization before your application
|
-- keep settings and values requiring initialization before your application
|
||||||
@ -306,6 +344,9 @@ instance YesodAuthPersist App
|
|||||||
instance RenderMessage App FormMessage where
|
instance RenderMessage App FormMessage where
|
||||||
renderMessage _ _ = defaultFormMessage
|
renderMessage _ _ = defaultFormMessage
|
||||||
|
|
||||||
|
unsafeHandler :: App -> Handler a -> IO a
|
||||||
|
unsafeHandler = Unsafe.fakeHandlerGetLogger appLogger
|
||||||
|
|
||||||
-- Note: Some functionality previously present in the scaffolding has been
|
-- Note: Some functionality previously present in the scaffolding has been
|
||||||
-- moved to documentation in the Wiki. Following are some hopefully helpful
|
-- moved to documentation in the Wiki. Following are some hopefully helpful
|
||||||
-- links:
|
-- links:
|
||||||
@ -423,6 +464,7 @@ Flag library-only
|
|||||||
Default: False
|
Default: False
|
||||||
|
|
||||||
library
|
library
|
||||||
|
hs-source-dirs: ., app
|
||||||
exposed-modules: Application
|
exposed-modules: Application
|
||||||
Foundation
|
Foundation
|
||||||
Import
|
Import
|
||||||
@ -458,7 +500,7 @@ library
|
|||||||
|
|
||||||
build-depends: base >= 4 && < 5
|
build-depends: base >= 4 && < 5
|
||||||
, yesod >= 1.4.1 && < 1.5
|
, yesod >= 1.4.1 && < 1.5
|
||||||
, yesod-core >= 1.4.0 && < 1.5
|
, yesod-core >= 1.4.6 && < 1.5
|
||||||
, yesod-auth >= 1.4.0 && < 1.5
|
, yesod-auth >= 1.4.0 && < 1.5
|
||||||
, yesod-static >= 1.4.0.3 && < 1.5
|
, yesod-static >= 1.4.0.3 && < 1.5
|
||||||
, yesod-form >= 1.4.0 && < 1.5
|
, yesod-form >= 1.4.0 && < 1.5
|
||||||
@ -695,40 +737,51 @@ import Yesod.Static (staticFiles)
|
|||||||
staticFiles (appStaticDir compileTimeAppSettings)
|
staticFiles (appStaticDir compileTimeAppSettings)
|
||||||
|
|
||||||
{-# START_FILE app/DevelMain.hs #-}
|
{-# START_FILE app/DevelMain.hs #-}
|
||||||
-- | Development version to be run inside GHCi.
|
-- | Running your app inside GHCi.
|
||||||
--
|
--
|
||||||
-- start this up with:
|
-- To start up GHCi for usage with Yesod, first make sure you are in dev mode:
|
||||||
--
|
--
|
||||||
-- cabal repl --ghc-options="-O0 -fobject-code"
|
-- > cabal configure -fdev
|
||||||
--
|
--
|
||||||
-- run with:
|
-- Note that @yesod devel@ automatically sets the dev flag.
|
||||||
|
-- Now launch the repl:
|
||||||
--
|
--
|
||||||
-- :l DevelMain
|
-- > cabal repl --ghc-options="-O0 -fobject-code"
|
||||||
-- DevelMain.update
|
|
||||||
--
|
--
|
||||||
-- You will need to add these packages to your .cabal file
|
-- To start your app, run:
|
||||||
-- * foreign-store >= 0.1 (very light-weight)
|
--
|
||||||
-- * warp (you already depend on this, it just isn't in your .cabal file)
|
-- > :l DevelMain
|
||||||
|
-- > DevelMain.update
|
||||||
|
--
|
||||||
|
-- You can also call @DevelMain.shutdown@ to stop the app
|
||||||
|
--
|
||||||
|
-- You will need to add the foreign-store package to your .cabal file.
|
||||||
|
-- It is very light-weight.
|
||||||
--
|
--
|
||||||
-- If you don't use cabal repl, you will need
|
-- If you don't use cabal repl, you will need
|
||||||
-- to add settings to your .ghci file.
|
-- to run the following in GHCi or to add it to
|
||||||
|
-- your .ghci file.
|
||||||
--
|
--
|
||||||
-- :set -DDEVELOPMENT
|
-- :set -DDEVELOPMENT
|
||||||
--
|
--
|
||||||
-- There is more information about using ghci
|
-- There is more information about this approach,
|
||||||
-- on the wiki: https://github.com/yesodweb/yesod/wiki/ghci
|
-- on the wiki: https://github.com/yesodweb/yesod/wiki/ghci
|
||||||
|
|
||||||
module DevelMain where
|
module DevelMain where
|
||||||
|
|
||||||
import Application (getApplicationDev)
|
import Prelude
|
||||||
|
import Application (getApplicationRepl, shutdownApp)
|
||||||
|
|
||||||
import Control.Exception (finally)
|
import Control.Exception (finally)
|
||||||
|
import Control.Monad ((>=>))
|
||||||
import Control.Concurrent
|
import Control.Concurrent
|
||||||
import Data.IORef
|
import Data.IORef
|
||||||
import Foreign.Store
|
import Foreign.Store
|
||||||
import Network.Wai.Handler.Warp
|
import Network.Wai.Handler.Warp
|
||||||
|
import GHC.Word
|
||||||
|
|
||||||
-- | Start or restart the server.
|
-- | Start or restart the server.
|
||||||
|
-- newStore is from foreign-store.
|
||||||
-- A Store holds onto some data across ghci reloads
|
-- A Store holds onto some data across ghci reloads
|
||||||
update :: IO ()
|
update :: IO ()
|
||||||
update = do
|
update = do
|
||||||
@ -741,27 +794,48 @@ update = do
|
|||||||
_ <- storeAction (Store tidStoreNum) (newIORef tid)
|
_ <- storeAction (Store tidStoreNum) (newIORef tid)
|
||||||
return ()
|
return ()
|
||||||
-- server is already running
|
-- server is already running
|
||||||
Just tidStore ->
|
Just tidStore -> restartAppInNewThread tidStore
|
||||||
-- shut the server down with killThread and wait for the done signal
|
|
||||||
modifyStoredIORef tidStore $ \tid -> do
|
|
||||||
killThread tid
|
|
||||||
withStore doneStore takeMVar >> readStore doneStore >>= start
|
|
||||||
where
|
where
|
||||||
|
doneStore :: Store (MVar ())
|
||||||
doneStore = Store 0
|
doneStore = Store 0
|
||||||
tidStoreNum = 1
|
|
||||||
|
|
||||||
modifyStoredIORef :: Store (IORef a) -> (a -> IO a) -> IO ()
|
-- shut the server down with killThread and wait for the done signal
|
||||||
modifyStoredIORef store f = withStore store $ \ref -> do
|
restartAppInNewThread :: Store (IORef ThreadId) -> IO ()
|
||||||
v <- readIORef ref
|
restartAppInNewThread tidStore = modifyStoredIORef tidStore $ \tid -> do
|
||||||
f v >>= writeIORef ref
|
killThread tid
|
||||||
|
withStore doneStore takeMVar
|
||||||
|
readStore doneStore >>= start
|
||||||
|
|
||||||
-- | Start the server in a separate thread.
|
|
||||||
start :: MVar () -- ^ Written to when the thread is killed.
|
-- | Start the server in a separate thread.
|
||||||
-> IO ThreadId
|
start :: MVar () -- ^ Written to when the thread is killed.
|
||||||
start done = do
|
-> IO ThreadId
|
||||||
(settings,app) <- getApplicationDev
|
start done = do
|
||||||
forkIO (finally (runSettings settings app)
|
(port, site, app) <- getApplicationRepl
|
||||||
(putMVar done ()))
|
forkIO (finally (runSettings (setPort port defaultSettings) app)
|
||||||
|
-- Note that this implies concurrency
|
||||||
|
-- between shutdownApp and the next app that is starting.
|
||||||
|
-- Normally this should be fine
|
||||||
|
(putMVar done () >> shutdownApp site))
|
||||||
|
|
||||||
|
-- | kill the server
|
||||||
|
shutdown :: IO ()
|
||||||
|
shutdown = do
|
||||||
|
mtidStore <- lookupStore tidStoreNum
|
||||||
|
case mtidStore of
|
||||||
|
-- no server running
|
||||||
|
Nothing -> putStrLn "no Yesod app running"
|
||||||
|
Just tidStore -> do
|
||||||
|
withStore tidStore $ readIORef >=> killThread
|
||||||
|
putStrLn "Yesod app is shutdown"
|
||||||
|
|
||||||
|
tidStoreNum :: Word32
|
||||||
|
tidStoreNum = 1
|
||||||
|
|
||||||
|
modifyStoredIORef :: Store (IORef a) -> (a -> IO a) -> IO ()
|
||||||
|
modifyStoredIORef store f = withStore store $ \ref -> do
|
||||||
|
v <- readIORef ref
|
||||||
|
f v >>= writeIORef ref
|
||||||
|
|
||||||
{-# START_FILE app/devel.hs #-}
|
{-# START_FILE app/devel.hs #-}
|
||||||
{-# LANGUAGE PackageImports #-}
|
{-# LANGUAGE PackageImports #-}
|
||||||
@ -9032,7 +9106,7 @@ module TestImport
|
|||||||
import Application (makeFoundation)
|
import Application (makeFoundation)
|
||||||
import ClassyPrelude as X
|
import ClassyPrelude as X
|
||||||
import Database.Persist as X hiding (get)
|
import Database.Persist as X hiding (get)
|
||||||
import Database.Persist.Sql (SqlPersistM, runSqlPersistMPool)
|
import Database.Persist.Sql (SqlPersistM, SqlBackend, runSqlPersistMPool, rawExecute, rawSql, unSingle, connEscapeName)
|
||||||
import Foundation as X
|
import Foundation as X
|
||||||
import Model as X
|
import Model as X
|
||||||
import Test.Hspec as X
|
import Test.Hspec as X
|
||||||
@ -9041,8 +9115,12 @@ import Yesod.Test as X
|
|||||||
|
|
||||||
runDB :: SqlPersistM a -> YesodExample App a
|
runDB :: SqlPersistM a -> YesodExample App a
|
||||||
runDB query = do
|
runDB query = do
|
||||||
pool <- fmap appConnPool getTestYesod
|
app <- getTestYesod
|
||||||
liftIO $ runSqlPersistMPool query pool
|
liftIO $ runDBWithApp app query
|
||||||
|
|
||||||
|
runDBWithApp :: App -> SqlPersistM a -> IO a
|
||||||
|
runDBWithApp app query = runSqlPersistMPool query (appConnPool app)
|
||||||
|
|
||||||
|
|
||||||
withApp :: SpecWith App -> Spec
|
withApp :: SpecWith App -> Spec
|
||||||
withApp = before $ do
|
withApp = before $ do
|
||||||
@ -9050,5 +9128,24 @@ withApp = before $ do
|
|||||||
["config/test-settings.yml", "config/settings.yml"]
|
["config/test-settings.yml", "config/settings.yml"]
|
||||||
[]
|
[]
|
||||||
ignoreEnv
|
ignoreEnv
|
||||||
makeFoundation settings
|
foundation <- makeFoundation settings
|
||||||
|
wipeDB foundation
|
||||||
|
return foundation
|
||||||
|
|
||||||
|
-- This function will truncate all of the tables in your database.
|
||||||
|
-- 'withApp' calls it before each test, creating a clean environment for each
|
||||||
|
-- spec to run in.
|
||||||
|
wipeDB :: App -> IO ()
|
||||||
|
wipeDB app = do
|
||||||
|
runDBWithApp app $ do
|
||||||
|
tables <- getTables
|
||||||
|
sqlBackend <- ask
|
||||||
|
|
||||||
|
let escapedTables = map (connEscapeName sqlBackend . DBName) tables
|
||||||
|
query = "TRUNCATE TABLE " ++ (intercalate ", " escapedTables)
|
||||||
|
rawExecute query []
|
||||||
|
|
||||||
|
getTables :: MonadIO m => ReaderT SqlBackend m [Text]
|
||||||
|
getTables = do
|
||||||
|
tables <- rawSql "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';" []
|
||||||
|
return $ map unSingle tables
|
||||||
|
|||||||
@ -32,6 +32,11 @@ module Application
|
|||||||
, appMain
|
, appMain
|
||||||
, develMain
|
, develMain
|
||||||
, makeFoundation
|
, makeFoundation
|
||||||
|
-- * for DevelMain
|
||||||
|
, getApplicationRepl
|
||||||
|
, shutdownApp
|
||||||
|
-- * for GHCI
|
||||||
|
, handler
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Monad.Logger (liftLoc)
|
import Control.Monad.Logger (liftLoc)
|
||||||
@ -40,7 +45,7 @@ import Language.Haskell.TH.Syntax (qLocation)
|
|||||||
import Network.Wai.Handler.Warp (Settings, defaultSettings,
|
import Network.Wai.Handler.Warp (Settings, defaultSettings,
|
||||||
defaultShouldDisplayException,
|
defaultShouldDisplayException,
|
||||||
runSettings, setHost,
|
runSettings, setHost,
|
||||||
setOnException, setPort)
|
setOnException, setPort, getPort)
|
||||||
import Network.Wai.Middleware.RequestLogger (Destination (Logger),
|
import Network.Wai.Middleware.RequestLogger (Destination (Logger),
|
||||||
IPAddrSource (..),
|
IPAddrSource (..),
|
||||||
OutputFormat (..), destination,
|
OutputFormat (..), destination,
|
||||||
@ -112,12 +117,15 @@ warpSettings foundation =
|
|||||||
-- | For yesod devel, return the Warp settings and WAI Application.
|
-- | For yesod devel, return the Warp settings and WAI Application.
|
||||||
getApplicationDev :: IO (Settings, Application)
|
getApplicationDev :: IO (Settings, Application)
|
||||||
getApplicationDev = do
|
getApplicationDev = do
|
||||||
settings <- loadAppSettings [configSettingsYml] [] useEnv
|
settings <- getAppSettings
|
||||||
foundation <- makeFoundation settings
|
foundation <- makeFoundation settings
|
||||||
app <- makeApplication foundation
|
|
||||||
wsettings <- getDevSettings $ warpSettings foundation
|
wsettings <- getDevSettings $ warpSettings foundation
|
||||||
|
app <- makeApplication foundation
|
||||||
return (wsettings, app)
|
return (wsettings, app)
|
||||||
|
|
||||||
|
getAppSettings :: IO AppSettings
|
||||||
|
getAppSettings = loadAppSettings [configSettingsYml] [] useEnv
|
||||||
|
|
||||||
-- | main function for use by yesod devel
|
-- | main function for use by yesod devel
|
||||||
develMain :: IO ()
|
develMain :: IO ()
|
||||||
develMain = develMainHelper getApplicationDev
|
develMain = develMainHelper getApplicationDev
|
||||||
@ -142,14 +150,39 @@ appMain = do
|
|||||||
-- Run the application with Warp
|
-- Run the application with Warp
|
||||||
runSettings (warpSettings foundation) app
|
runSettings (warpSettings foundation) app
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------------------------------------------
|
||||||
|
-- Functions for DevelMain.hs (a way to run the app from GHCi)
|
||||||
|
--------------------------------------------------------------
|
||||||
|
getApplicationRepl :: IO (Int, App, Application)
|
||||||
|
getApplicationRepl = do
|
||||||
|
settings <- getAppSettings
|
||||||
|
foundation <- makeFoundation settings
|
||||||
|
wsettings <- getDevSettings $ warpSettings foundation
|
||||||
|
app1 <- makeApplication foundation
|
||||||
|
return (getPort wsettings, foundation, app1)
|
||||||
|
|
||||||
|
shutdownApp :: App -> IO ()
|
||||||
|
shutdownApp _ = return ()
|
||||||
|
|
||||||
|
|
||||||
|
---------------------------------------------
|
||||||
|
-- Functions for use in development with GHCi
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
-- | Run a handler
|
||||||
|
handler :: Handler a -> IO a
|
||||||
|
handler h = getAppSettings >>= makeFoundation >>= flip unsafeHandler h
|
||||||
|
|
||||||
{-# START_FILE Foundation.hs #-}
|
{-# START_FILE Foundation.hs #-}
|
||||||
module Foundation where
|
module Foundation where
|
||||||
|
|
||||||
import Import.NoFoundation
|
import Import.NoFoundation
|
||||||
import Text.Hamlet (hamletFile)
|
import Text.Hamlet (hamletFile)
|
||||||
import Text.Jasmine (minifym)
|
import Text.Jasmine (minifym)
|
||||||
import Yesod.Core.Types (Logger)
|
import Yesod.Core.Types (Logger)
|
||||||
import Yesod.Default.Util (addStaticContentExternal)
|
import Yesod.Default.Util (addStaticContentExternal)
|
||||||
|
import qualified Yesod.Core.Unsafe as Unsafe
|
||||||
|
|
||||||
-- | The foundation datatype for your application. This can be a good place to
|
-- | The foundation datatype for your application. This can be a good place to
|
||||||
-- keep settings and values requiring initialization before your application
|
-- keep settings and values requiring initialization before your application
|
||||||
@ -244,6 +277,9 @@ instance Yesod App where
|
|||||||
instance RenderMessage App FormMessage where
|
instance RenderMessage App FormMessage where
|
||||||
renderMessage _ _ = defaultFormMessage
|
renderMessage _ _ = defaultFormMessage
|
||||||
|
|
||||||
|
unsafeHandler :: App -> Handler a -> IO a
|
||||||
|
unsafeHandler = Unsafe.fakeHandlerGetLogger appLogger
|
||||||
|
|
||||||
-- Note: Some functionality previously present in the scaffolding has been
|
-- Note: Some functionality previously present in the scaffolding has been
|
||||||
-- moved to documentation in the Wiki. Following are some hopefully helpful
|
-- moved to documentation in the Wiki. Following are some hopefully helpful
|
||||||
-- links:
|
-- links:
|
||||||
@ -346,6 +382,7 @@ Flag library-only
|
|||||||
Default: False
|
Default: False
|
||||||
|
|
||||||
library
|
library
|
||||||
|
hs-source-dirs: ., app
|
||||||
exposed-modules: Application
|
exposed-modules: Application
|
||||||
Foundation
|
Foundation
|
||||||
Import
|
Import
|
||||||
@ -380,7 +417,7 @@ library
|
|||||||
|
|
||||||
build-depends: base >= 4 && < 5
|
build-depends: base >= 4 && < 5
|
||||||
, yesod >= 1.4.1 && < 1.5
|
, yesod >= 1.4.1 && < 1.5
|
||||||
, yesod-core >= 1.4.0 && < 1.5
|
, yesod-core >= 1.4.6 && < 1.5
|
||||||
, yesod-static >= 1.4.0.3 && < 1.5
|
, yesod-static >= 1.4.0.3 && < 1.5
|
||||||
, yesod-form >= 1.4.0 && < 1.5
|
, yesod-form >= 1.4.0 && < 1.5
|
||||||
, classy-prelude >= 0.10.2
|
, classy-prelude >= 0.10.2
|
||||||
@ -604,40 +641,51 @@ import Yesod.Static (staticFiles)
|
|||||||
staticFiles (appStaticDir compileTimeAppSettings)
|
staticFiles (appStaticDir compileTimeAppSettings)
|
||||||
|
|
||||||
{-# START_FILE app/DevelMain.hs #-}
|
{-# START_FILE app/DevelMain.hs #-}
|
||||||
-- | Development version to be run inside GHCi.
|
-- | Running your app inside GHCi.
|
||||||
--
|
--
|
||||||
-- start this up with:
|
-- To start up GHCi for usage with Yesod, first make sure you are in dev mode:
|
||||||
--
|
--
|
||||||
-- cabal repl --ghc-options="-O0 -fobject-code"
|
-- > cabal configure -fdev
|
||||||
--
|
--
|
||||||
-- run with:
|
-- Note that @yesod devel@ automatically sets the dev flag.
|
||||||
|
-- Now launch the repl:
|
||||||
--
|
--
|
||||||
-- :l DevelMain
|
-- > cabal repl --ghc-options="-O0 -fobject-code"
|
||||||
-- DevelMain.update
|
|
||||||
--
|
--
|
||||||
-- You will need to add these packages to your .cabal file
|
-- To start your app, run:
|
||||||
-- * foreign-store >= 0.1 (very light-weight)
|
--
|
||||||
-- * warp (you already depend on this, it just isn't in your .cabal file)
|
-- > :l DevelMain
|
||||||
|
-- > DevelMain.update
|
||||||
|
--
|
||||||
|
-- You can also call @DevelMain.shutdown@ to stop the app
|
||||||
|
--
|
||||||
|
-- You will need to add the foreign-store package to your .cabal file.
|
||||||
|
-- It is very light-weight.
|
||||||
--
|
--
|
||||||
-- If you don't use cabal repl, you will need
|
-- If you don't use cabal repl, you will need
|
||||||
-- to add settings to your .ghci file.
|
-- to run the following in GHCi or to add it to
|
||||||
|
-- your .ghci file.
|
||||||
--
|
--
|
||||||
-- :set -DDEVELOPMENT
|
-- :set -DDEVELOPMENT
|
||||||
--
|
--
|
||||||
-- There is more information about using ghci
|
-- There is more information about this approach,
|
||||||
-- on the wiki: https://github.com/yesodweb/yesod/wiki/ghci
|
-- on the wiki: https://github.com/yesodweb/yesod/wiki/ghci
|
||||||
|
|
||||||
module DevelMain where
|
module DevelMain where
|
||||||
|
|
||||||
import Application (getApplicationDev)
|
import Prelude
|
||||||
|
import Application (getApplicationRepl, shutdownApp)
|
||||||
|
|
||||||
import Control.Exception (finally)
|
import Control.Exception (finally)
|
||||||
|
import Control.Monad ((>=>))
|
||||||
import Control.Concurrent
|
import Control.Concurrent
|
||||||
import Data.IORef
|
import Data.IORef
|
||||||
import Foreign.Store
|
import Foreign.Store
|
||||||
import Network.Wai.Handler.Warp
|
import Network.Wai.Handler.Warp
|
||||||
|
import GHC.Word
|
||||||
|
|
||||||
-- | Start or restart the server.
|
-- | Start or restart the server.
|
||||||
|
-- newStore is from foreign-store.
|
||||||
-- A Store holds onto some data across ghci reloads
|
-- A Store holds onto some data across ghci reloads
|
||||||
update :: IO ()
|
update :: IO ()
|
||||||
update = do
|
update = do
|
||||||
@ -650,27 +698,48 @@ update = do
|
|||||||
_ <- storeAction (Store tidStoreNum) (newIORef tid)
|
_ <- storeAction (Store tidStoreNum) (newIORef tid)
|
||||||
return ()
|
return ()
|
||||||
-- server is already running
|
-- server is already running
|
||||||
Just tidStore ->
|
Just tidStore -> restartAppInNewThread tidStore
|
||||||
-- shut the server down with killThread and wait for the done signal
|
|
||||||
modifyStoredIORef tidStore $ \tid -> do
|
|
||||||
killThread tid
|
|
||||||
withStore doneStore takeMVar >> readStore doneStore >>= start
|
|
||||||
where
|
where
|
||||||
|
doneStore :: Store (MVar ())
|
||||||
doneStore = Store 0
|
doneStore = Store 0
|
||||||
tidStoreNum = 1
|
|
||||||
|
|
||||||
modifyStoredIORef :: Store (IORef a) -> (a -> IO a) -> IO ()
|
-- shut the server down with killThread and wait for the done signal
|
||||||
modifyStoredIORef store f = withStore store $ \ref -> do
|
restartAppInNewThread :: Store (IORef ThreadId) -> IO ()
|
||||||
v <- readIORef ref
|
restartAppInNewThread tidStore = modifyStoredIORef tidStore $ \tid -> do
|
||||||
f v >>= writeIORef ref
|
killThread tid
|
||||||
|
withStore doneStore takeMVar
|
||||||
|
readStore doneStore >>= start
|
||||||
|
|
||||||
-- | Start the server in a separate thread.
|
|
||||||
start :: MVar () -- ^ Written to when the thread is killed.
|
-- | Start the server in a separate thread.
|
||||||
-> IO ThreadId
|
start :: MVar () -- ^ Written to when the thread is killed.
|
||||||
start done = do
|
-> IO ThreadId
|
||||||
(settings,app) <- getApplicationDev
|
start done = do
|
||||||
forkIO (finally (runSettings settings app)
|
(port, site, app) <- getApplicationRepl
|
||||||
(putMVar done ()))
|
forkIO (finally (runSettings (setPort port defaultSettings) app)
|
||||||
|
-- Note that this implies concurrency
|
||||||
|
-- between shutdownApp and the next app that is starting.
|
||||||
|
-- Normally this should be fine
|
||||||
|
(putMVar done () >> shutdownApp site))
|
||||||
|
|
||||||
|
-- | kill the server
|
||||||
|
shutdown :: IO ()
|
||||||
|
shutdown = do
|
||||||
|
mtidStore <- lookupStore tidStoreNum
|
||||||
|
case mtidStore of
|
||||||
|
-- no server running
|
||||||
|
Nothing -> putStrLn "no Yesod app running"
|
||||||
|
Just tidStore -> do
|
||||||
|
withStore tidStore $ readIORef >=> killThread
|
||||||
|
putStrLn "Yesod app is shutdown"
|
||||||
|
|
||||||
|
tidStoreNum :: Word32
|
||||||
|
tidStoreNum = 1
|
||||||
|
|
||||||
|
modifyStoredIORef :: Store (IORef a) -> (a -> IO a) -> IO ()
|
||||||
|
modifyStoredIORef store f = withStore store $ \ref -> do
|
||||||
|
v <- readIORef ref
|
||||||
|
f v >>= writeIORef ref
|
||||||
|
|
||||||
{-# START_FILE app/devel.hs #-}
|
{-# START_FILE app/devel.hs #-}
|
||||||
{-# LANGUAGE PackageImports #-}
|
{-# LANGUAGE PackageImports #-}
|
||||||
|
|||||||
@ -32,6 +32,12 @@ module Application
|
|||||||
, appMain
|
, appMain
|
||||||
, develMain
|
, develMain
|
||||||
, makeFoundation
|
, makeFoundation
|
||||||
|
-- * for DevelMain
|
||||||
|
, getApplicationRepl
|
||||||
|
, shutdownApp
|
||||||
|
-- * for GHCI
|
||||||
|
, handler
|
||||||
|
, db
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Monad.Logger (liftLoc, runLoggingT)
|
import Control.Monad.Logger (liftLoc, runLoggingT)
|
||||||
@ -42,7 +48,7 @@ import Language.Haskell.TH.Syntax (qLocation)
|
|||||||
import Network.Wai.Handler.Warp (Settings, defaultSettings,
|
import Network.Wai.Handler.Warp (Settings, defaultSettings,
|
||||||
defaultShouldDisplayException,
|
defaultShouldDisplayException,
|
||||||
runSettings, setHost,
|
runSettings, setHost,
|
||||||
setOnException, setPort)
|
setOnException, setPort, getPort)
|
||||||
import Network.Wai.Middleware.RequestLogger (Destination (Logger),
|
import Network.Wai.Middleware.RequestLogger (Destination (Logger),
|
||||||
IPAddrSource (..),
|
IPAddrSource (..),
|
||||||
OutputFormat (..), destination,
|
OutputFormat (..), destination,
|
||||||
@ -131,12 +137,15 @@ warpSettings foundation =
|
|||||||
-- | For yesod devel, return the Warp settings and WAI Application.
|
-- | For yesod devel, return the Warp settings and WAI Application.
|
||||||
getApplicationDev :: IO (Settings, Application)
|
getApplicationDev :: IO (Settings, Application)
|
||||||
getApplicationDev = do
|
getApplicationDev = do
|
||||||
settings <- loadAppSettings [configSettingsYml] [] useEnv
|
settings <- getAppSettings
|
||||||
foundation <- makeFoundation settings
|
foundation <- makeFoundation settings
|
||||||
app <- makeApplication foundation
|
|
||||||
wsettings <- getDevSettings $ warpSettings foundation
|
wsettings <- getDevSettings $ warpSettings foundation
|
||||||
|
app <- makeApplication foundation
|
||||||
return (wsettings, app)
|
return (wsettings, app)
|
||||||
|
|
||||||
|
getAppSettings :: IO AppSettings
|
||||||
|
getAppSettings = loadAppSettings [configSettingsYml] [] useEnv
|
||||||
|
|
||||||
-- | main function for use by yesod devel
|
-- | main function for use by yesod devel
|
||||||
develMain :: IO ()
|
develMain :: IO ()
|
||||||
develMain = develMainHelper getApplicationDev
|
develMain = develMainHelper getApplicationDev
|
||||||
@ -161,6 +170,34 @@ appMain = do
|
|||||||
-- Run the application with Warp
|
-- Run the application with Warp
|
||||||
runSettings (warpSettings foundation) app
|
runSettings (warpSettings foundation) app
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------------------------------------------
|
||||||
|
-- Functions for DevelMain.hs (a way to run the app from GHCi)
|
||||||
|
--------------------------------------------------------------
|
||||||
|
getApplicationRepl :: IO (Int, App, Application)
|
||||||
|
getApplicationRepl = do
|
||||||
|
settings <- getAppSettings
|
||||||
|
foundation <- makeFoundation settings
|
||||||
|
wsettings <- getDevSettings $ warpSettings foundation
|
||||||
|
app1 <- makeApplication foundation
|
||||||
|
return (getPort wsettings, foundation, app1)
|
||||||
|
|
||||||
|
shutdownApp :: App -> IO ()
|
||||||
|
shutdownApp _ = return ()
|
||||||
|
|
||||||
|
|
||||||
|
---------------------------------------------
|
||||||
|
-- Functions for use in development with GHCi
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
-- | Run a handler
|
||||||
|
handler :: Handler a -> IO a
|
||||||
|
handler h = getAppSettings >>= makeFoundation >>= flip unsafeHandler h
|
||||||
|
|
||||||
|
-- | Run DB queries
|
||||||
|
db :: ReaderT SqlBackend (HandlerT App IO) a -> IO a
|
||||||
|
db = handler . runDB
|
||||||
|
|
||||||
{-# START_FILE Foundation.hs #-}
|
{-# START_FILE Foundation.hs #-}
|
||||||
module Foundation where
|
module Foundation where
|
||||||
|
|
||||||
@ -171,6 +208,7 @@ import Text.Jasmine (minifym)
|
|||||||
import Yesod.Auth.BrowserId (authBrowserId)
|
import Yesod.Auth.BrowserId (authBrowserId)
|
||||||
import Yesod.Default.Util (addStaticContentExternal)
|
import Yesod.Default.Util (addStaticContentExternal)
|
||||||
import Yesod.Core.Types (Logger)
|
import Yesod.Core.Types (Logger)
|
||||||
|
import qualified Yesod.Core.Unsafe as Unsafe
|
||||||
|
|
||||||
-- | The foundation datatype for your application. This can be a good place to
|
-- | The foundation datatype for your application. This can be a good place to
|
||||||
-- keep settings and values requiring initialization before your application
|
-- keep settings and values requiring initialization before your application
|
||||||
@ -306,6 +344,9 @@ instance YesodAuthPersist App
|
|||||||
instance RenderMessage App FormMessage where
|
instance RenderMessage App FormMessage where
|
||||||
renderMessage _ _ = defaultFormMessage
|
renderMessage _ _ = defaultFormMessage
|
||||||
|
|
||||||
|
unsafeHandler :: App -> Handler a -> IO a
|
||||||
|
unsafeHandler = Unsafe.fakeHandlerGetLogger appLogger
|
||||||
|
|
||||||
-- Note: Some functionality previously present in the scaffolding has been
|
-- Note: Some functionality previously present in the scaffolding has been
|
||||||
-- moved to documentation in the Wiki. Following are some hopefully helpful
|
-- moved to documentation in the Wiki. Following are some hopefully helpful
|
||||||
-- links:
|
-- links:
|
||||||
@ -423,6 +464,7 @@ Flag library-only
|
|||||||
Default: False
|
Default: False
|
||||||
|
|
||||||
library
|
library
|
||||||
|
hs-source-dirs: ., app
|
||||||
exposed-modules: Application
|
exposed-modules: Application
|
||||||
Foundation
|
Foundation
|
||||||
Import
|
Import
|
||||||
@ -458,7 +500,7 @@ library
|
|||||||
|
|
||||||
build-depends: base >= 4 && < 5
|
build-depends: base >= 4 && < 5
|
||||||
, yesod >= 1.4.1 && < 1.5
|
, yesod >= 1.4.1 && < 1.5
|
||||||
, yesod-core >= 1.4.0 && < 1.5
|
, yesod-core >= 1.4.6 && < 1.5
|
||||||
, yesod-auth >= 1.4.0 && < 1.5
|
, yesod-auth >= 1.4.0 && < 1.5
|
||||||
, yesod-static >= 1.4.0.3 && < 1.5
|
, yesod-static >= 1.4.0.3 && < 1.5
|
||||||
, yesod-form >= 1.4.0 && < 1.5
|
, yesod-form >= 1.4.0 && < 1.5
|
||||||
@ -695,40 +737,51 @@ import Yesod.Static (staticFiles)
|
|||||||
staticFiles (appStaticDir compileTimeAppSettings)
|
staticFiles (appStaticDir compileTimeAppSettings)
|
||||||
|
|
||||||
{-# START_FILE app/DevelMain.hs #-}
|
{-# START_FILE app/DevelMain.hs #-}
|
||||||
-- | Development version to be run inside GHCi.
|
-- | Running your app inside GHCi.
|
||||||
--
|
--
|
||||||
-- start this up with:
|
-- To start up GHCi for usage with Yesod, first make sure you are in dev mode:
|
||||||
--
|
--
|
||||||
-- cabal repl --ghc-options="-O0 -fobject-code"
|
-- > cabal configure -fdev
|
||||||
--
|
--
|
||||||
-- run with:
|
-- Note that @yesod devel@ automatically sets the dev flag.
|
||||||
|
-- Now launch the repl:
|
||||||
--
|
--
|
||||||
-- :l DevelMain
|
-- > cabal repl --ghc-options="-O0 -fobject-code"
|
||||||
-- DevelMain.update
|
|
||||||
--
|
--
|
||||||
-- You will need to add these packages to your .cabal file
|
-- To start your app, run:
|
||||||
-- * foreign-store >= 0.1 (very light-weight)
|
--
|
||||||
-- * warp (you already depend on this, it just isn't in your .cabal file)
|
-- > :l DevelMain
|
||||||
|
-- > DevelMain.update
|
||||||
|
--
|
||||||
|
-- You can also call @DevelMain.shutdown@ to stop the app
|
||||||
|
--
|
||||||
|
-- You will need to add the foreign-store package to your .cabal file.
|
||||||
|
-- It is very light-weight.
|
||||||
--
|
--
|
||||||
-- If you don't use cabal repl, you will need
|
-- If you don't use cabal repl, you will need
|
||||||
-- to add settings to your .ghci file.
|
-- to run the following in GHCi or to add it to
|
||||||
|
-- your .ghci file.
|
||||||
--
|
--
|
||||||
-- :set -DDEVELOPMENT
|
-- :set -DDEVELOPMENT
|
||||||
--
|
--
|
||||||
-- There is more information about using ghci
|
-- There is more information about this approach,
|
||||||
-- on the wiki: https://github.com/yesodweb/yesod/wiki/ghci
|
-- on the wiki: https://github.com/yesodweb/yesod/wiki/ghci
|
||||||
|
|
||||||
module DevelMain where
|
module DevelMain where
|
||||||
|
|
||||||
import Application (getApplicationDev)
|
import Prelude
|
||||||
|
import Application (getApplicationRepl, shutdownApp)
|
||||||
|
|
||||||
import Control.Exception (finally)
|
import Control.Exception (finally)
|
||||||
|
import Control.Monad ((>=>))
|
||||||
import Control.Concurrent
|
import Control.Concurrent
|
||||||
import Data.IORef
|
import Data.IORef
|
||||||
import Foreign.Store
|
import Foreign.Store
|
||||||
import Network.Wai.Handler.Warp
|
import Network.Wai.Handler.Warp
|
||||||
|
import GHC.Word
|
||||||
|
|
||||||
-- | Start or restart the server.
|
-- | Start or restart the server.
|
||||||
|
-- newStore is from foreign-store.
|
||||||
-- A Store holds onto some data across ghci reloads
|
-- A Store holds onto some data across ghci reloads
|
||||||
update :: IO ()
|
update :: IO ()
|
||||||
update = do
|
update = do
|
||||||
@ -741,27 +794,48 @@ update = do
|
|||||||
_ <- storeAction (Store tidStoreNum) (newIORef tid)
|
_ <- storeAction (Store tidStoreNum) (newIORef tid)
|
||||||
return ()
|
return ()
|
||||||
-- server is already running
|
-- server is already running
|
||||||
Just tidStore ->
|
Just tidStore -> restartAppInNewThread tidStore
|
||||||
-- shut the server down with killThread and wait for the done signal
|
|
||||||
modifyStoredIORef tidStore $ \tid -> do
|
|
||||||
killThread tid
|
|
||||||
withStore doneStore takeMVar >> readStore doneStore >>= start
|
|
||||||
where
|
where
|
||||||
|
doneStore :: Store (MVar ())
|
||||||
doneStore = Store 0
|
doneStore = Store 0
|
||||||
tidStoreNum = 1
|
|
||||||
|
|
||||||
modifyStoredIORef :: Store (IORef a) -> (a -> IO a) -> IO ()
|
-- shut the server down with killThread and wait for the done signal
|
||||||
modifyStoredIORef store f = withStore store $ \ref -> do
|
restartAppInNewThread :: Store (IORef ThreadId) -> IO ()
|
||||||
v <- readIORef ref
|
restartAppInNewThread tidStore = modifyStoredIORef tidStore $ \tid -> do
|
||||||
f v >>= writeIORef ref
|
killThread tid
|
||||||
|
withStore doneStore takeMVar
|
||||||
|
readStore doneStore >>= start
|
||||||
|
|
||||||
-- | Start the server in a separate thread.
|
|
||||||
start :: MVar () -- ^ Written to when the thread is killed.
|
-- | Start the server in a separate thread.
|
||||||
-> IO ThreadId
|
start :: MVar () -- ^ Written to when the thread is killed.
|
||||||
start done = do
|
-> IO ThreadId
|
||||||
(settings,app) <- getApplicationDev
|
start done = do
|
||||||
forkIO (finally (runSettings settings app)
|
(port, site, app) <- getApplicationRepl
|
||||||
(putMVar done ()))
|
forkIO (finally (runSettings (setPort port defaultSettings) app)
|
||||||
|
-- Note that this implies concurrency
|
||||||
|
-- between shutdownApp and the next app that is starting.
|
||||||
|
-- Normally this should be fine
|
||||||
|
(putMVar done () >> shutdownApp site))
|
||||||
|
|
||||||
|
-- | kill the server
|
||||||
|
shutdown :: IO ()
|
||||||
|
shutdown = do
|
||||||
|
mtidStore <- lookupStore tidStoreNum
|
||||||
|
case mtidStore of
|
||||||
|
-- no server running
|
||||||
|
Nothing -> putStrLn "no Yesod app running"
|
||||||
|
Just tidStore -> do
|
||||||
|
withStore tidStore $ readIORef >=> killThread
|
||||||
|
putStrLn "Yesod app is shutdown"
|
||||||
|
|
||||||
|
tidStoreNum :: Word32
|
||||||
|
tidStoreNum = 1
|
||||||
|
|
||||||
|
modifyStoredIORef :: Store (IORef a) -> (a -> IO a) -> IO ()
|
||||||
|
modifyStoredIORef store f = withStore store $ \ref -> do
|
||||||
|
v <- readIORef ref
|
||||||
|
f v >>= writeIORef ref
|
||||||
|
|
||||||
{-# START_FILE app/devel.hs #-}
|
{-# START_FILE app/devel.hs #-}
|
||||||
{-# LANGUAGE PackageImports #-}
|
{-# LANGUAGE PackageImports #-}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
name: yesod-bin
|
name: yesod-bin
|
||||||
version: 1.4.3.3
|
version: 1.4.3.4
|
||||||
license: MIT
|
license: MIT
|
||||||
license-file: LICENSE
|
license-file: LICENSE
|
||||||
author: Michael Snoyman <michael@snoyman.com>
|
author: Michael Snoyman <michael@snoyman.com>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user