Scaffolding uses PersistConfig

This commit is contained in:
Michael Snoyman 2011-09-24 21:51:44 +03:00
parent 2fd9f18cf2
commit 933f0086d2
5 changed files with 57 additions and 111 deletions

View File

@ -1,15 +1,11 @@
{-# LANGUAGE OverloadedStrings #-}
module Yesod.Config module Yesod.Config
( AppConfig(..) ( AppConfig(..)
, PostgresConf(..)
, SqliteConf(..)
, MongoConf(..)
, loadConfig , loadConfig
, loadPostgresql , withYamlEnvironment
, loadSqlite
, loadMongo
) where ) where
import Control.Monad (join, forM) import Control.Monad (join)
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Data.Object import Data.Object
import Data.Object.Yaml import Data.Object.Yaml
@ -25,28 +21,6 @@ data AppConfig e = AppConfig
, appRoot :: Text , appRoot :: Text
} deriving (Show) } deriving (Show)
-- separate types means more code here, but it's easier to use in the
-- scaffold
-- | Information required to connect to a postgres database
data PostgresConf = PostgresConf
{ pgConnStr :: Text
, pgPoolSize :: Int
}
-- | Information required to connect to a sqlite database
data SqliteConf = SqliteConf
{ sqlDatabase :: Text
, sqlPoolSize :: Int
}
-- | Information required to connect to a mongo database
data MongoConf = MongoConf
{ mgDatabase :: String
, mgHost :: String
, mgPoolSize :: Int
}
-- | Load an @'AppConfig'@ from @config\/settings.yml@. -- | Load an @'AppConfig'@ from @config\/settings.yml@.
-- --
-- Some examples: -- Some examples:
@ -77,7 +51,8 @@ data MongoConf = MongoConf
-- > -- automatically. -- > -- automatically.
-- --
loadConfig :: Show e => e -> IO (AppConfig e) loadConfig :: Show e => e -> IO (AppConfig e)
loadConfig env = withYamlEnvironment "config/settings.yml" env $ \e -> do loadConfig env = withYamlEnvironment "config/settings.yml" env $ \e' -> do
e <- maybe (fail "Expected map") return $ fromMapping e'
let mssl = lookupScalar "ssl" e let mssl = lookupScalar "ssl" e
let mhost = lookupScalar "host" e let mhost = lookupScalar "host" e
let mport = lookupScalar "port" e let mport = lookupScalar "port" e
@ -89,94 +64,49 @@ loadConfig env = withYamlEnvironment "config/settings.yml" env $ \e -> do
approot <- case (mhost, mapproot) of approot <- case (mhost, mapproot) of
(_ , Just ar) -> return ar (_ , Just ar) -> return ar
(Just host, _ ) -> return $ (if ssl then "https://" else "http://") ++ host ++ (addPort ssl port) (Just host, _ ) -> return $ T.concat
[ if ssl then "https://" else "http://"
, host
, addPort ssl port
]
_ -> fail "You must supply either a host or approot" _ -> fail "You must supply either a host or approot"
return $ AppConfig return $ AppConfig
{ appEnv = env { appEnv = env
, appPort = port , appPort = port
, appRoot = T.pack approot , appRoot = approot
} }
where where
toBool :: String -> Bool toBool :: Text -> Bool
toBool = (`elem` ["true", "TRUE", "yes", "YES", "Y", "1"]) toBool = (`elem` ["true", "TRUE", "yes", "YES", "Y", "1"])
addPort :: Bool -> Int -> String addPort :: Bool -> Int -> Text
addPort True 443 = "" addPort True 443 = ""
addPort False 80 = "" addPort False 80 = ""
addPort _ p = ":" ++ show p addPort _ p = T.pack $ ':' : show p
-- | Load a @'PostgresConf'@ from @config\/postgresql.yml@.
--
-- > Production:
-- > user: jsmith
-- > password: secret
-- > host: localhost
-- > port: 5432
-- > database: some_db
-- > poolsize: 100
--
loadPostgresql :: Show e => e -> IO PostgresConf
loadPostgresql env = withYamlEnvironment "config/postgresql.yml" env $ \e -> do
db <- lookupScalar "database" e
pool <- safeRead "poolsize" =<< lookupScalar "poolsize" e
-- TODO: default host/port?
connparts <- forM ["user", "password", "host", "port"] $ \k -> do
v <- lookupScalar k e
return $ k ++ "=" ++ v ++ " "
conn <- return $ concat connparts
return $ PostgresConf (T.pack $ conn ++ " dbname=" ++ db) pool
-- | Load a @'SqliteConf'@ from @config\/sqlite.yml@.
--
-- > Production:
-- > database: foo.s3db
-- > poolsize: 100
--
loadSqlite :: Show e => e -> IO SqliteConf
loadSqlite env = withYamlEnvironment "config/sqlite.yml" env $ \e -> do
db <- lookupScalar "database" e
pool <- safeRead "poolsize" =<< lookupScalar "poolsize" e
return $ SqliteConf (T.pack db) pool
-- | Load a @'MongoConf'@ from @config\/mongoDB.yml@.
--
-- > Production:
-- > database: some_db
-- > host: localhost
-- > poolsize: 100
--
loadMongo :: Show e => e -> IO MongoConf
loadMongo env = withYamlEnvironment "config/mongoDB.yml" env $ \e -> do
db <- lookupScalar "database" e
host <- lookupScalar "host" e
pool <- safeRead "poolsize" =<< lookupScalar "poolsize" e
return $ MongoConf db host pool
-- | Loads the configuration block in the passed file named by the -- | Loads the configuration block in the passed file named by the
-- passed environment, yeilds to the passed function as a mapping. -- passed environment, yeilds to the passed function as a mapping.
-- --
-- Errors in the case of a bad load or if your function returns -- Errors in the case of a bad load or if your function returns
-- @Nothing@. -- @Nothing@.
withYamlEnvironment :: (IsYamlScalar v, Show e) withYamlEnvironment :: Show e
=> FilePath -- ^ the yaml file => FilePath -- ^ the yaml file
-> e -- ^ the environment you want to load -> e -- ^ the environment you want to load
-> ([(String, Object String v)] -> IO a) -- ^ what to do with the mapping -> (TextObject -> IO a) -- ^ what to do with the mapping
-> IO a -> IO a
withYamlEnvironment fp env f = do withYamlEnvironment fp env f = do
obj <- join $ decodeFile fp obj <- join $ decodeFile fp
envs <- fromMapping obj envs <- fromMapping obj
conf <- lookupMapping (show env) envs conf <- maybe (fail $ "Could not find environment: " ++ show env) return
$ lookup (T.pack $ show env) envs
f conf f conf
-- | Returns 'fail' if read fails -- | Returns 'fail' if read fails
safeRead :: Monad m => String -> String -> m Int safeRead :: Monad m => String -> Text -> m Int
safeRead name s = case reads s of safeRead name t = case reads s of
(i, _):_ -> return i (i, _):_ -> return i
[] -> fail $ concat ["Invalid value for ", name, ": ", s] [] -> fail $ concat ["Invalid value for ", name, ": ", s]
where
s = T.unpack t

View File

@ -66,9 +66,9 @@ scaffold = do
backendC <- prompt $ flip elem $ map (return . toLower . head . show) backends backendC <- prompt $ flip elem $ map (return . toLower . head . show) backends
let (backend, importGenericDB, dbMonad, importPersist, mkPersistSettings) = let (backend, importGenericDB, dbMonad, importPersist, mkPersistSettings) =
case backendC of case backendC of
"s" -> (Sqlite, "GenericSql", "SqlPersist", "Sqlite\n", "sqlSettings") "s" -> (Sqlite, "GenericSql", "SqlPersist", "Sqlite", "sqlSettings")
"p" -> (Postgresql, "GenericSql", "SqlPersist", "Postgresql\n", "sqlSettings") "p" -> (Postgresql, "GenericSql", "SqlPersist", "Postgresql", "sqlSettings")
"m" -> (MongoDB, "MongoDB", "Action", "MongoDB\nimport Control.Applicative (Applicative)\n", "MkPersistSettings { mpsBackend = ConT ''Action }") "m" -> (MongoDB, "MongoDB", "Action", "MongoDB", "MkPersistSettings { mpsBackend = ConT ''Action }")
"t" -> (Tiny, "","","",undefined) "t" -> (Tiny, "","","",undefined)
_ -> error $ "Invalid backend: " ++ backendC _ -> error $ "Invalid backend: " ++ backendC
(modelImports) = case backend of (modelImports) = case backend of
@ -84,7 +84,26 @@ scaffold = do
let runMigration = let runMigration =
case backend of case backend of
MongoDB -> "" MongoDB -> ""
_ -> "\n runConnectionPool (runMigration migrateAll) p" _ -> "\n Database.Persist.Base.runPool dbconf (runMigration migrateAll) p"
let importMigration =
case backend of
MongoDB -> ""
_ -> "\nimport Database.Persist.GenericSql (runMigration)"
let dbConfigFile =
case backend of
MongoDB -> "mongoDB"
Sqlite -> "sqlite"
Postgresql -> "postgres"
Tiny -> error "Accessing dbConfigFile for Tiny"
let configPersist =
case backend of
MongoDB -> "MongoConf"
Sqlite -> "SqliteConf"
Postgresql -> "PostgresConf"
Tiny -> error "Accessing configPersist for Tiny"
putStrLn "That's it! I'm creating your files now..." putStrLn "That's it! I'm creating your files now..."

View File

@ -15,9 +15,9 @@ import Yesod.Auth
import Yesod.Default.Config import Yesod.Default.Config
import Yesod.Default.Main import Yesod.Default.Main
import Yesod.Logger (Logger) import Yesod.Logger (Logger)
import Database.Persist.~importGenericDB~
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Data.Dynamic (Dynamic, toDyn) import Data.Dynamic (Dynamic, toDyn)
import qualified Database.Persist.Base~importMigration~
-- Import all relevant handler modules here. -- Import all relevant handler modules here.
import Handler.Root import Handler.Root
@ -46,7 +46,9 @@ with~sitearg~ conf logger f = do
#else #else
s <- staticDevel Settings.staticDir s <- staticDevel Settings.staticDir
#endif #endif
Settings.withConnectionPool conf $ \p -> do~runMigration~ dbconf <- withYamlEnvironment "config/~dbConfigFile~.yml" (appEnv conf)
$ either error return . Database.Persist.Base.loadConfig
Database.Persist.Base.withPool (dbconf :: Settings.PersistConfig) $ \p -> do~runMigration~
let h = ~sitearg~ conf logger s p let h = ~sitearg~ conf logger s p
defaultRunner f h defaultRunner f h

View File

@ -27,6 +27,7 @@ import Yesod.Default.Util (addStaticContentExternal)
import Yesod.Logger (Logger, logLazyText) import Yesod.Logger (Logger, logLazyText)
import qualified Settings import qualified Settings
import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy as L
import qualified Database.Persist.Base
import Database.Persist.~importGenericDB~ import Database.Persist.~importGenericDB~
import Settings (widgetFile) import Settings (widgetFile)
import Model import Model
@ -47,7 +48,7 @@ data ~sitearg~ = ~sitearg~
{ settings :: AppConfig DefaultEnv { settings :: AppConfig DefaultEnv
, getLogger :: Logger , getLogger :: Logger
, getStatic :: Static -- ^ Settings for static file serving. , getStatic :: Static -- ^ Settings for static file serving.
, connPool :: Settings.ConnectionPool -- ^ Database connection pool. , connPool :: Database.Persist.Base.PersistConfigPool Settings.PersistConfig -- ^ Database connection pool.
} }
-- Set up i18n messages. See the message folder. -- Set up i18n messages. See the message folder.
@ -121,7 +122,7 @@ instance Yesod ~sitearg~ where
instance YesodPersist ~sitearg~ where instance YesodPersist ~sitearg~ where
type YesodPersistBackend ~sitearg~ = ~dbMonad~ type YesodPersistBackend ~sitearg~ = ~dbMonad~
runDB f = liftIOHandler runDB f = liftIOHandler
$ fmap connPool getYesod >>= Settings.runConnectionPool f $ fmap connPool getYesod >>= Database.Persist.Base.runPool (undefined :: Settings.PersistConfig) f
instance YesodAuth ~sitearg~ where instance YesodAuth ~sitearg~ where
type AuthId ~sitearg~ = UserId type AuthId ~sitearg~ = UserId

View File

@ -8,21 +8,21 @@
-- declared in the Foundation.hs file. -- declared in the Foundation.hs file.
module Settings module Settings
( widgetFile ( widgetFile
, ConnectionPool , PersistConfig
, withConnectionPool
, runConnectionPool
, staticRoot , staticRoot
, staticDir , staticDir
) where ) where
import Text.Shakespeare.Text (st) import Text.Shakespeare.Text (st)
import Language.Haskell.TH.Syntax import Language.Haskell.TH.Syntax
import Database.Persist.~importPersist~ import Database.Persist.~importPersist~ (~configPersist~)
import Yesod (liftIO, MonadControlIO)
import Yesod.Default.Config import Yesod.Default.Config
import qualified Yesod.Default.Util import qualified Yesod.Default.Util
import Data.Text (Text) import Data.Text (Text)
-- | Which Persistent backend this site is using.
type PersistConfig = ~configPersist~
-- Static setting below. Changing these requires a recompile -- Static setting below. Changing these requires a recompile
-- | The location of static files on your system. This is a file system -- | The location of static files on your system. This is a file system
@ -50,12 +50,6 @@ staticRoot conf = [st|#{appRoot conf}/static|]
-- The rest of this file contains settings which rarely need changing by a -- The rest of this file contains settings which rarely need changing by a
-- user. -- user.
-- The next functions are for allocating a connection pool and running
-- database actions using a pool, respectively. They are used internally
-- by the scaffolded application, and therefore you will rarely need to use
-- them yourself.
~withConnectionPool~
widgetFile :: String -> Q Exp widgetFile :: String -> Q Exp
#if PRODUCTION #if PRODUCTION
widgetFile = Yesod.Default.Util.widgetFileProduction widgetFile = Yesod.Default.Util.widgetFileProduction