From d3c7ccebe16434034bf8674cddc4724b57417534 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Sat, 10 Sep 2011 23:21:35 -0400 Subject: [PATCH 01/44] Provide sane defaults with less scaffolding Yesod.Settings provides the typical definitions for AppConfig, AppEnvironment, and loadConfig (read from YAML). Yesod.Main provides a single defaultMain function which accepts your usual withSiteArg function and runs via Warp calling the now provided loadConfig to figure out what to do. Note: Yesod re-exports Y.Settings and Y.Main -- This is probably not the right thing to do since it would cause collisions with users not using the provided functionality (including all existing users). --- yesod-core/Yesod/Main.hs | 51 ++++++++++++++++++++++++++++++++++++ yesod-core/Yesod/Settings.hs | 42 +++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 yesod-core/Yesod/Main.hs create mode 100644 yesod-core/Yesod/Settings.hs diff --git a/yesod-core/Yesod/Main.hs b/yesod-core/Yesod/Main.hs new file mode 100644 index 00000000..fda8f99a --- /dev/null +++ b/yesod-core/Yesod/Main.hs @@ -0,0 +1,51 @@ +{-# LANGUAGE CPP, DeriveDataTypeable #-} +module Yesod.Main (defaultMain) where + +import Yesod.Logger (Logger, makeLogger) +import Yesod.Settings (AppEnvironment(..), AppConfig(..), loadConfig) +import Network.Wai (Application) +import Network.Wai.Handler.Warp (run) +import System.Console.CmdArgs hiding (args) +import Data.Char (toUpper, toLower) + +defaultMain :: (AppConfig -> Logger -> (Application -> IO ()) -> IO ()) -> IO () +defaultMain withSite = do + logger <- makeLogger + args <- cmdArgs argConfig + env <- getAppEnv args + config <- loadConfig env + + let c = if port args /= 0 + then config { appPort = port args } + else config + + withSite c logger $ run (appPort c) + +data ArgConfig = ArgConfig + { environment :: String + , port :: Int + } deriving (Show, Data, Typeable) + +argConfig :: ArgConfig +argConfig = ArgConfig + { environment = def + &= help ("application environment, one of: " ++ (foldl1 (\a b -> a ++ ", " ++ b) environments)) + &= typ "ENVIRONMENT" + , port = def + &= help "the port to listen on" + &= typ "PORT" + } + +getAppEnv :: ArgConfig -> IO AppEnvironment +getAppEnv cfg = do + let e = if environment cfg /= "" + then environment cfg + else "development" + return $ read $ capitalize e + + where + capitalize [] = [] + capitalize (x:xs) = toUpper x : map toLower xs + +environments :: [String] +environments = map ((map toLower) . show) ([minBound..maxBound] :: [AppEnvironment]) diff --git a/yesod-core/Yesod/Settings.hs b/yesod-core/Yesod/Settings.hs new file mode 100644 index 00000000..840264ca --- /dev/null +++ b/yesod-core/Yesod/Settings.hs @@ -0,0 +1,42 @@ +module Yesod.Settings where + +import Control.Monad (join) +import Data.Object +import Data.Text (Text) + +import qualified Data.Object.Yaml as YAML +import qualified Data.Text as T + +data AppEnvironment = Development + | Test + | Staging + | Production + deriving (Eq, Show, Read, Enum, Bounded) + +data AppConfig = AppConfig + { appEnv :: AppEnvironment + , appPort :: Int + , connectionPoolSize :: Int + , appRoot :: Text + } deriving (Show) + +loadConfig :: AppEnvironment -> IO AppConfig +loadConfig env = do + allSettings <- (join $ YAML.decodeFile ("config/settings.yml" :: String)) >>= fromMapping + settings <- lookupMapping (show env) allSettings + hostS <- lookupScalar "host" settings + port <- fmap read $ lookupScalar "port" settings + connectionPoolSizeS <- lookupScalar "connectionPoolSize" settings + + return $ AppConfig + { appEnv = env + , appPort = port + , appRoot = T.pack $ hostS ++ addPort port + , connectionPoolSize = read connectionPoolSizeS + } + + where + addPort :: Int -> String + addPort p = case env of + Production -> "" + _ -> ":" ++ show p From b5a3bd4671edbde2375eb902879f3f85a07eec7b Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Sat, 10 Sep 2011 23:32:41 -0400 Subject: [PATCH 02/44] Add dependencies and expose new modules --- yesod-core/yesod-core.cabal | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/yesod-core/yesod-core.cabal b/yesod-core/yesod-core.cabal index f33a1f88..9fa70f24 100644 --- a/yesod-core/yesod-core.cabal +++ b/yesod-core/yesod-core.cabal @@ -54,6 +54,10 @@ library , case-insensitive >= 0.2 && < 0.4 , parsec >= 2 && < 3.2 , directory >= 1 && < 1.2 + , cmdargs >= 0.8 && < 0.9 + , data-object >= 0.3 && < 0.4 + , data-object-yaml >= 0.3 && < 0.4 + , warp >= 0.4 && < 0.5 -- for logger. Probably logger should be a separate package , strict-concurrency >= 0.2.4 && < 0.2.5 @@ -65,6 +69,8 @@ library Yesod.Request Yesod.Widget Yesod.Message + Yesod.Settings + Yesod.Main other-modules: Yesod.Internal Yesod.Internal.Core Yesod.Internal.Session From 63d34a3bb46f612a8285cc0fbd954f19ed05f0c4 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Sat, 10 Sep 2011 23:33:17 -0400 Subject: [PATCH 03/44] Update scaffold for defaultMain --- yesod/Scaffolding/Scaffolder.hs | 4 +- yesod/scaffold/Application.hs.cg | 2 +- yesod/scaffold/Foundation.hs.cg | 4 +- yesod/scaffold/Settings.hs.cg | 66 +------------------------- yesod/scaffold/main.hs.cg | 67 +-------------------------- yesod/scaffold/tiny/Application.hs.cg | 3 +- yesod/scaffold/tiny/Foundation.hs.cg | 5 +- yesod/scaffold/tiny/Settings.hs.cg | 52 +-------------------- 8 files changed, 14 insertions(+), 189 deletions(-) diff --git a/yesod/Scaffolding/Scaffolder.hs b/yesod/Scaffolding/Scaffolder.hs index 287bb8a9..c8fe885d 100644 --- a/yesod/Scaffolding/Scaffolder.hs +++ b/yesod/Scaffolding/Scaffolder.hs @@ -95,8 +95,8 @@ scaffold = do Tiny -> "" settingsTextImport = case backend of - Postgresql -> "import Data.Text (Text, pack, concat)\nimport Prelude hiding (concat)" - _ -> "import Data.Text (Text, pack)" + Postgresql -> "import Data.Text (Text, concat)\nimport Prelude hiding (concat)" + _ -> "import Data.Text (Text)" packages = if backend == MongoDB diff --git a/yesod/scaffold/Application.hs.cg b/yesod/scaffold/Application.hs.cg index 03153b5a..8688fe02 100644 --- a/yesod/scaffold/Application.hs.cg +++ b/yesod/scaffold/Application.hs.cg @@ -68,7 +68,7 @@ withDevelAppPort = where go :: ((Int, Application) -> IO ()) -> IO () go f = do - conf <- Settings.loadConfig Settings.Development + conf <- loadConfig Development let port = appPort conf logger <- makeLogger logString logger $ "Devel application launched, listening on port " ++ show port diff --git a/yesod/scaffold/Foundation.hs.cg b/yesod/scaffold/Foundation.hs.cg index 3f000eb5..a925401f 100644 --- a/yesod/scaffold/Foundation.hs.cg +++ b/yesod/scaffold/Foundation.hs.cg @@ -45,7 +45,7 @@ import Text.Shakespeare.Text (stext) -- starts running, such as database connections. Every handler will have -- access to the data present here. data ~sitearg~ = ~sitearg~ - { settings :: Settings.AppConfig + { settings :: AppConfig , getLogger :: Logger , getStatic :: Static -- ^ Settings for static file serving. , connPool :: Settings.ConnectionPool -- ^ Database connection pool. @@ -75,7 +75,7 @@ mkYesodData "~sitearg~" $(parseRoutesFile "config/routes") -- Please see the documentation for the Yesod typeclass. There are a number -- of settings which can be configured by overriding methods here. instance Yesod ~sitearg~ where - approot = Settings.appRoot . settings + approot = appRoot . settings -- Place the session key file in the config folder encryptKey _ = fmap Just $ getKey "config/client_session_key.aes" diff --git a/yesod/scaffold/Settings.hs.cg b/yesod/scaffold/Settings.hs.cg index 9109198e..fdcf3c59 100644 --- a/yesod/scaffold/Settings.hs.cg +++ b/yesod/scaffold/Settings.hs.cg @@ -18,9 +18,6 @@ module Settings , runConnectionPool , staticRoot , staticDir - , loadConfig - , AppEnvironment(..) - , AppConfig(..) ) where import qualified Text.Hamlet as S @@ -32,6 +29,7 @@ import Text.Shakespeare.Text (st) import Language.Haskell.TH.Syntax import Database.Persist.~importPersist~ import Yesod (liftIO, MonadControlIO, addWidget, addCassius, addJulius, addLucius, whamletFile) +import Yesod.Settings import Data.Monoid (mempty) import System.Directory (doesFileExist) ~settingsTextImport~ @@ -39,68 +37,6 @@ import Data.Object import qualified Data.Object.Yaml as YAML import Control.Monad (join) -data AppEnvironment = Test - | Development - | Staging - | Production - deriving (Eq, Show, Read, Enum, Bounded) - --- | Dynamic per-environment configuration loaded from the YAML file Settings.yaml. --- Use dynamic settings to avoid the need to re-compile the application (between staging and production environments). --- --- By convention these settings should be overwritten by any command line arguments. --- See config/Foundation.hs for command line arguments --- Command line arguments provide some convenience but are also required for hosting situations where a setting is read from the environment (appPort on Heroku). --- -data AppConfig = AppConfig { - appEnv :: AppEnvironment - - , appPort :: Int - - -- | Your application will keep a connection pool and take connections from - -- there as necessary instead of continually creating new connections. This - -- value gives the maximum number of connections to be open at a given time. - -- If your application requests a connection when all connections are in - -- use, that request will fail. Try to choose a number that will work well - -- with the system resources available to you while providing enough - -- connections for your expected load. - -- - -- Connections are returned to the pool as quickly as possible by - -- Yesod to avoid resource exhaustion. A connection is only considered in - -- use while within a call to runDB. - , connectionPoolSize :: Int - - -- | The base URL for your application. This will usually be different for - -- development and production. Yesod automatically constructs URLs for you, - -- so this value must be accurate to create valid links. - -- Please note that there is no trailing slash. - -- - -- You probably want to change this! If your domain name was "yesod.com", - -- you would probably want it to be: - -- > "http://yesod.com" - , appRoot :: Text -} deriving (Show) - -loadConfig :: AppEnvironment -> IO AppConfig -loadConfig env = do - allSettings <- (join $ YAML.decodeFile ("config/settings.yml" :: String)) >>= fromMapping - settings <- lookupMapping (show env) allSettings - hostS <- lookupScalar "host" settings - port <- fmap read $ lookupScalar "port" settings - connectionPoolSizeS <- lookupScalar "connectionPoolSize" settings - return $ AppConfig { - appEnv = env - , appPort = port - , appRoot = pack $ hostS ++ addPort port - , connectionPoolSize = read connectionPoolSizeS - } - where - addPort :: Int -> String -#ifdef PRODUCTION - addPort _ = "" -#else - addPort p = ":" ++ (show p) -#endif -- Static setting below. Changing these requires a recompile diff --git a/yesod/scaffold/main.hs.cg b/yesod/scaffold/main.hs.cg index eede5e55..1e7f88ac 100644 --- a/yesod/scaffold/main.hs.cg +++ b/yesod/scaffold/main.hs.cg @@ -1,68 +1,5 @@ -{-# LANGUAGE CPP, DeriveDataTypeable #-} -import Settings (AppEnvironment(..), AppConfig(..), loadConfig) +import Yesod.Main (defaultMain) import Application (with~sitearg~) -import Network.Wai.Handler.Warp (run) -import System.Console.CmdArgs hiding (args) -import Data.Char (toUpper, toLower) - -#ifndef PRODUCTION -import Network.Wai.Middleware.Debug (debugHandle) -import Yesod.Logger (logString, logLazyText, flushLogger, makeLogger) -#else -import Yesod.Logger (makeLogger) -#endif main :: IO () -main = do - logger <- makeLogger - args <- cmdArgs argConfig - env <- getAppEnv args - config <- loadConfig env - let c = if port args /= 0 - then config { appPort = port args } - else config - -#if PRODUCTION - with~sitearg~ c logger $ run (appPort c) -#else - logString logger $ (show env) ++ " application launched, listening on port " ++ show (appPort c) - with~sitearg~ c logger $ run (appPort c) . debugHandle (logHandle logger) - flushLogger logger - - where - logHandle logger msg = logLazyText logger msg >> flushLogger logger -#endif - -data ArgConfig = ArgConfig - { environment :: String - , port :: Int - } deriving (Show, Data, Typeable) - -argConfig :: ArgConfig -argConfig = ArgConfig - { environment = def - &= help ("application environment, one of: " ++ (foldl1 (\a b -> a ++ ", " ++ b) environments)) - &= typ "ENVIRONMENT" - , port = def - &= typ "PORT" - } - -environments :: [String] -environments = map ((map toLower) . show) ([minBound..maxBound] :: [AppEnvironment]) - --- | retrieve the -e environment option -getAppEnv :: ArgConfig -> IO AppEnvironment -getAppEnv cfg = do - let e = if environment cfg /= "" - then environment cfg - else -#if PRODUCTION - "production" -#else - "development" -#endif - return $ read $ capitalize e - - where - capitalize [] = [] - capitalize (x:xs) = toUpper x : map toLower xs +main = defaultMain with~sitearg~ diff --git a/yesod/scaffold/tiny/Application.hs.cg b/yesod/scaffold/tiny/Application.hs.cg index 99e4d7f9..e4e2aaef 100644 --- a/yesod/scaffold/tiny/Application.hs.cg +++ b/yesod/scaffold/tiny/Application.hs.cg @@ -11,6 +11,7 @@ module Application import Foundation import Settings import Yesod.Static +import Yesod.Settings import Yesod.Logger (makeLogger, flushLogger, Logger, logLazyText, logString) import Data.ByteString (ByteString) import Network.Wai (Application) @@ -54,7 +55,7 @@ withDevelAppPort = where go :: ((Int, Application) -> IO ()) -> IO () go f = do - conf <- Settings.loadConfig Settings.Development + conf <- loadConfig Development let port = appPort conf logger <- makeLogger logString logger $ "Devel application launched, listening on port " ++ show port diff --git a/yesod/scaffold/tiny/Foundation.hs.cg b/yesod/scaffold/tiny/Foundation.hs.cg index 72993157..5e950805 100644 --- a/yesod/scaffold/tiny/Foundation.hs.cg +++ b/yesod/scaffold/tiny/Foundation.hs.cg @@ -14,6 +14,7 @@ module Foundation ) where import Yesod.Core +import Yesod.Settings (AppConfig(..)) import Yesod.Static (Static, base64md5, StaticRoute(..)) import Settings.StaticFiles import Yesod.Logger (Logger, logLazyText) @@ -32,7 +33,7 @@ import Web.ClientSession (getKey) -- starts running, such as database connections. Every handler will have -- access to the data present here. data ~sitearg~ = ~sitearg~ - { settings :: Settings.AppConfig + { settings :: AppConfig , getLogger :: Logger , getStatic :: Static -- ^ Settings for static file serving. } @@ -61,7 +62,7 @@ mkYesodData "~sitearg~" $(parseRoutesFile "config/routes") -- Please see the documentation for the Yesod typeclass. There are a number -- of settings which can be configured by overriding methods here. instance Yesod ~sitearg~ where - approot = Settings.appRoot . settings + approot = appRoot . settings -- Place the session key file in the config folder encryptKey _ = fmap Just $ getKey "config/client_session_key.aes" diff --git a/yesod/scaffold/tiny/Settings.hs.cg b/yesod/scaffold/tiny/Settings.hs.cg index bec5d248..73e226ad 100644 --- a/yesod/scaffold/tiny/Settings.hs.cg +++ b/yesod/scaffold/tiny/Settings.hs.cg @@ -14,9 +14,6 @@ module Settings , widgetFile , staticRoot , staticDir - , loadConfig - , AppEnvironment(..) - , AppConfig(..) ) where import qualified Text.Hamlet as S @@ -27,6 +24,7 @@ import qualified Text.Shakespeare.Text as S import Text.Shakespeare.Text (st) import Language.Haskell.TH.Syntax import Yesod.Widget (addWidget, addCassius, addJulius, addLucius, whamletFile) +import Yesod.Settings import Data.Monoid (mempty) import System.Directory (doesFileExist) ~settingsTextImport~ @@ -34,54 +32,6 @@ import Data.Object import qualified Data.Object.Yaml as YAML import Control.Monad (join) -data AppEnvironment = Test - | Development - | Staging - | Production - deriving (Eq, Show, Read, Enum, Bounded) - --- | Dynamic per-environment configuration loaded from the YAML file Settings.yaml. --- Use dynamic settings to avoid the need to re-compile the application (between staging and production environments). --- --- By convention these settings should be overwritten by any command line arguments. --- See config/~sitearg~.hs for command line arguments --- Command line arguments provide some convenience but are also required for hosting situations where a setting is read from the environment (appPort on Heroku). --- -data AppConfig = AppConfig { - appEnv :: AppEnvironment - - , appPort :: Int - - -- | The base URL for your application. This will usually be different for - -- development and production. Yesod automatically constructs URLs for you, - -- so this value must be accurate to create valid links. - -- Please note that there is no trailing slash. - -- - -- You probably want to change this! If your domain name was "yesod.com", - -- you would probably want it to be: - -- > "http://yesod.com" - , appRoot :: Text -} deriving (Show) - -loadConfig :: AppEnvironment -> IO AppConfig -loadConfig env = do - allSettings <- (join $ YAML.decodeFile ("config/settings.yml" :: String)) >>= fromMapping - settings <- lookupMapping (show env) allSettings - hostS <- lookupScalar "host" settings - port <- fmap read $ lookupScalar "port" settings - return $ AppConfig { - appEnv = env - , appPort = port - , appRoot = pack $ hostS ++ addPort port - } - where - addPort :: Int -> String -#ifdef PRODUCTION - addPort _ = "" -#else - addPort p = ":" ++ (show p) -#endif - -- | The location of static files on your system. This is a file system -- path. The default value works properly with your scaffolded site. staticDir :: FilePath From 4654776fa42fdfebba758f95114858809c3ee652 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Sat, 10 Sep 2011 23:33:31 -0400 Subject: [PATCH 04/44] Re-export new modules --- yesod/Yesod.hs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/yesod/Yesod.hs b/yesod/Yesod.hs index 7724a345..6fc6ee98 100644 --- a/yesod/Yesod.hs +++ b/yesod/Yesod.hs @@ -7,6 +7,8 @@ module Yesod , module Yesod.Form , module Yesod.Json , module Yesod.Persist + , module Yesod.Settings + , module Yesod.Main -- * Running your application , warp , warpDebug @@ -46,6 +48,8 @@ import Text.Julius import Yesod.Form import Yesod.Json import Yesod.Persist +import Yesod.Settings +import Yesod.Main import Network.Wai (Application) import Network.Wai.Middleware.Debug import Control.Monad.Trans.Class (lift) From 86c8abb853fd25f65143ade8f49816e05a777484 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Sun, 11 Sep 2011 00:16:43 -0400 Subject: [PATCH 05/44] Limit exports --- yesod-core/Yesod/Settings.hs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/yesod-core/Yesod/Settings.hs b/yesod-core/Yesod/Settings.hs index 840264ca..4d84e144 100644 --- a/yesod-core/Yesod/Settings.hs +++ b/yesod-core/Yesod/Settings.hs @@ -1,4 +1,8 @@ -module Yesod.Settings where +module Yesod.Settings + ( AppEnvironment(..) + , AppConfig(..) + , loadConfig + ) where import Control.Monad (join) import Data.Object From dcb27df1fc0ce6c58c9655818c42d51d67e51380 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Sun, 11 Sep 2011 00:17:07 -0400 Subject: [PATCH 06/44] Be a little more configurable --- yesod-core/Yesod/Main.hs | 91 +++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/yesod-core/Yesod/Main.hs b/yesod-core/Yesod/Main.hs index fda8f99a..fab4ead0 100644 --- a/yesod-core/Yesod/Main.hs +++ b/yesod-core/Yesod/Main.hs @@ -1,5 +1,9 @@ -{-# LANGUAGE CPP, DeriveDataTypeable #-} -module Yesod.Main (defaultMain) where +{-# LANGUAGE DeriveDataTypeable #-} +module Yesod.Main + ( defaultMain + , fromArgs + , fromArgsWith + ) where import Yesod.Logger (Logger, makeLogger) import Yesod.Settings (AppEnvironment(..), AppConfig(..), loadConfig) @@ -8,44 +12,65 @@ import Network.Wai.Handler.Warp (run) import System.Console.CmdArgs hiding (args) import Data.Char (toUpper, toLower) -defaultMain :: (AppConfig -> Logger -> (Application -> IO ()) -> IO ()) -> IO () -defaultMain withSite = do - logger <- makeLogger - args <- cmdArgs argConfig - env <- getAppEnv args - config <- loadConfig env - - let c = if port args /= 0 - then config { appPort = port args } - else config - - withSite c logger $ run (appPort c) - data ArgConfig = ArgConfig { environment :: String , port :: Int } deriving (Show, Data, Typeable) -argConfig :: ArgConfig -argConfig = ArgConfig - { environment = def - &= help ("application environment, one of: " ++ (foldl1 (\a b -> a ++ ", " ++ b) environments)) - &= typ "ENVIRONMENT" - , port = def - &= help "the port to listen on" - &= typ "PORT" - } +-- | Load an @'AppConfig'@ using the provided function, then start your +-- app via Warp on the configured port. +-- +-- > -- main.hs +-- > import Application (withMySite) +-- > import Yesod.Main (defaultMain, fromArgs) +-- > +-- > main :: IO () +-- > main = defaultMain fromArgs withMySite +-- +defaultMain :: IO AppConfig -> (AppConfig -> Logger -> (Application -> IO ()) -> IO ()) -> IO () +defaultMain load withSite = do + config <- load + logger <- makeLogger + withSite config logger $ run (appPort config) -getAppEnv :: ArgConfig -> IO AppEnvironment -getAppEnv cfg = do - let e = if environment cfg /= "" - then environment cfg - else "development" - return $ read $ capitalize e +-- | Call the @'Yesod.Settings.loadConfig'@ function for the environment +-- passed on the commandline (or the default, \"development\") and +-- override the port if passed. +fromArgs :: IO AppConfig +fromArgs = fromArgsWith loadConfig + +-- | Same, but allows one to provide their own custom @'loadConfig'@ +fromArgsWith :: (AppEnvironment -> IO AppConfig) -> IO AppConfig +fromArgsWith load = do + args <- cmdArgs argConfig + + let env = read + $ capitalize + $ if environment args /= "" + then environment args + else "development" + + config <- load env + + let c = if port args /= 0 + then config { appPort = port args } + else config + + return $ config where + argConfig :: ArgConfig + argConfig = ArgConfig + { environment = def + &= help ("application environment, one of: " ++ (foldl1 (\a b -> a ++ ", " ++ b) environments)) + &= typ "ENVIRONMENT" + , port = def + &= help "the port to listen on" + &= typ "PORT" + } + + environments :: [String] + environments = map ((map toLower) . show) ([minBound..maxBound] :: [AppEnvironment]) + capitalize [] = [] capitalize (x:xs) = toUpper x : map toLower xs - -environments :: [String] -environments = map ((map toLower) . show) ([minBound..maxBound] :: [AppEnvironment]) From fed3f0c0fa72ea97e680c1918e227330f6bc3214 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Sun, 11 Sep 2011 00:35:34 -0400 Subject: [PATCH 07/44] Fix logical error --- yesod-core/Yesod/Main.hs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/yesod-core/Yesod/Main.hs b/yesod-core/Yesod/Main.hs index fab4ead0..bba1fd3e 100644 --- a/yesod-core/Yesod/Main.hs +++ b/yesod-core/Yesod/Main.hs @@ -47,16 +47,14 @@ fromArgsWith load = do let env = read $ capitalize $ if environment args /= "" - then environment args - else "development" + then environment args + else "development" config <- load env - let c = if port args /= 0 - then config { appPort = port args } - else config - - return $ config + return $ if port args /= 0 + then config { appPort = port args } + else config where argConfig :: ArgConfig From 6360cb5e62535b3cf88f9e6c46c5995a9064df78 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Sun, 11 Sep 2011 00:35:46 -0400 Subject: [PATCH 08/44] Update scaffold to use fromArgs --- yesod/scaffold/main.hs.cg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yesod/scaffold/main.hs.cg b/yesod/scaffold/main.hs.cg index 1e7f88ac..94f61c28 100644 --- a/yesod/scaffold/main.hs.cg +++ b/yesod/scaffold/main.hs.cg @@ -1,5 +1,5 @@ -import Yesod.Main (defaultMain) +import Yesod.Main (defaultMain, fromArgs) import Application (with~sitearg~) main :: IO () -main = defaultMain with~sitearg~ +main = defaultMain fromArgs with~sitearg~ From bd843a7acc629bc0cf592e9ab5fdae8861944070 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Sun, 11 Sep 2011 12:30:22 -0400 Subject: [PATCH 09/44] Add typical connStr loading functions --- yesod-core/Yesod/Settings.hs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/yesod-core/Yesod/Settings.hs b/yesod-core/Yesod/Settings.hs index 4d84e144..f42862e9 100644 --- a/yesod-core/Yesod/Settings.hs +++ b/yesod-core/Yesod/Settings.hs @@ -1,12 +1,16 @@ +{-# LANGUAGE QuasiQuotes #-} module Yesod.Settings ( AppEnvironment(..) , AppConfig(..) , loadConfig + , loadPostgresqlConnStr + , loadSqliteConnStr ) where import Control.Monad (join) import Data.Object import Data.Text (Text) +import Text.Shakespeare.Text (st) import qualified Data.Object.Yaml as YAML import qualified Data.Text as T @@ -44,3 +48,22 @@ loadConfig env = do addPort p = case env of Production -> "" _ -> ":" ++ show p + +loadPostgresqlConnStr :: AppEnvironment -> IO Text +loadPostgresqlConnStr env = do + allSettings <- (join $ YAML.decodeFile ("config/postgesql.yml" :: String)) >>= fromMapping + settings <- lookupMapping (show env) allSettings + database <- lookupScalar "database" settings :: IO Text + + connPart <- fmap T.concat $ (flip mapM) ["user", "password", "host", "port"] $ \key -> do + value <- lookupScalar key settings + return $ [st| #{key}=#{value} |] + return $ [st|#{connPart} dbname=#{database}|] + +loadSqliteConnStr :: AppEnvironment -> IO Text +loadSqliteConnStr env = do + allSettings <- (join $ YAML.decodeFile ("config/sqlite.yml" :: String)) >>= fromMapping + settings <- lookupMapping (show env) allSettings + lookupScalar "database" settings + +-- TODO: Mongo From 80314a4027befb522e5922d78807a0e8e92a1b64 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Sun, 11 Sep 2011 12:30:31 -0400 Subject: [PATCH 10/44] Add shakespeare-text dep --- yesod-core/yesod-core.cabal | 1 + 1 file changed, 1 insertion(+) diff --git a/yesod-core/yesod-core.cabal b/yesod-core/yesod-core.cabal index 6b33e2f5..ce7a8c0c 100644 --- a/yesod-core/yesod-core.cabal +++ b/yesod-core/yesod-core.cabal @@ -38,6 +38,7 @@ library , shakespeare >= 0.10 && < 0.11 , shakespeare-js >= 0.10 && < 0.11 , shakespeare-css >= 0.10 && < 0.11 + , shakespeare-text >= 0.10 && < 0.11 , blaze-builder >= 0.2.1 && < 0.4 , transformers >= 0.2 && < 0.3 , clientsession >= 0.7.2 && < 0.8 From 256245cd2b186305ea3f22f08511d9ae88b89709 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Sun, 11 Sep 2011 13:06:53 -0400 Subject: [PATCH 11/44] Add mongo connection loader --- yesod-core/Yesod/Settings.hs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/yesod-core/Yesod/Settings.hs b/yesod-core/Yesod/Settings.hs index f42862e9..8e303972 100644 --- a/yesod-core/Yesod/Settings.hs +++ b/yesod-core/Yesod/Settings.hs @@ -1,10 +1,12 @@ -{-# LANGUAGE QuasiQuotes #-} +{-# OPTIONS -fno-warn-missing-signatures #-} +{-# LANGUAGE QuasiQuotes #-} module Yesod.Settings ( AppEnvironment(..) , AppConfig(..) , loadConfig , loadPostgresqlConnStr , loadSqliteConnStr + , loadMongoConnParams ) where import Control.Monad (join) @@ -63,7 +65,14 @@ loadPostgresqlConnStr env = do loadSqliteConnStr :: AppEnvironment -> IO Text loadSqliteConnStr env = do allSettings <- (join $ YAML.decodeFile ("config/sqlite.yml" :: String)) >>= fromMapping - settings <- lookupMapping (show env) allSettings + settings <- lookupMapping (show env) allSettings lookupScalar "database" settings --- TODO: Mongo +-- note: no type signature to avoid Persistent.MongoDB dep +--loadMongoConnParams :: AppEnvironment -> IO (Database, HostName) +loadMongoConnParams env = do + allSettings <- (join $ YAML.decodeFile ("config/mongoDB.yml" :: String)) >>= fromMapping + settings <- lookupMapping (show env) allSettings + database <- lookupScalar "database" settings + host <- lookupScalar "host" settings + return (database, host) From fa3fabcfbaadc8977dd888b4bd50e7da13e332a8 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Sun, 11 Sep 2011 14:57:06 -0400 Subject: [PATCH 12/44] Update scaffolding with new load connection functions Yesod.Settings provides load functions which were previously scaffolded. They load the ConsStrs for SQL and ConnParams for Mongo. This prevents the need for a lot of the imports in the scaffolding settings and simplifies the existing Text import. --- yesod/Scaffolding/Scaffolder.hs | 4 ---- yesod/scaffold/Settings.hs.cg | 5 +---- yesod/scaffold/mongoDBConnPool.cg | 12 ++---------- yesod/scaffold/postgresqlConnPool.cg | 15 +-------------- yesod/scaffold/sqliteConnPool.cg | 10 +--------- yesod/scaffold/tiny/Settings.hs.cg | 5 +---- 6 files changed, 6 insertions(+), 45 deletions(-) diff --git a/yesod/Scaffolding/Scaffolder.hs b/yesod/Scaffolding/Scaffolder.hs index c8fe885d..293032b6 100644 --- a/yesod/Scaffolding/Scaffolder.hs +++ b/yesod/Scaffolding/Scaffolder.hs @@ -94,10 +94,6 @@ scaffold = do MongoDB -> $(codegen $ "mongoDBConnPool") Tiny -> "" - settingsTextImport = case backend of - Postgresql -> "import Data.Text (Text, concat)\nimport Prelude hiding (concat)" - _ -> "import Data.Text (Text)" - packages = if backend == MongoDB then " , persistent-mongoDB >= 0.6.1 && < 0.7\n , mongoDB >= 1.1\n , bson >= 0.1.5\n" diff --git a/yesod/scaffold/Settings.hs.cg b/yesod/scaffold/Settings.hs.cg index fdcf3c59..36bd1576 100644 --- a/yesod/scaffold/Settings.hs.cg +++ b/yesod/scaffold/Settings.hs.cg @@ -32,10 +32,7 @@ import Yesod (liftIO, MonadControlIO, addWidget, addCassius, addJulius, addLuciu import Yesod.Settings import Data.Monoid (mempty) import System.Directory (doesFileExist) -~settingsTextImport~ -import Data.Object -import qualified Data.Object.Yaml as YAML -import Control.Monad (join) +import Data.Text (Text) -- Static setting below. Changing these requires a recompile diff --git a/yesod/scaffold/mongoDBConnPool.cg b/yesod/scaffold/mongoDBConnPool.cg index 57cb2aa0..4292d22f 100644 --- a/yesod/scaffold/mongoDBConnPool.cg +++ b/yesod/scaffold/mongoDBConnPool.cg @@ -3,14 +3,6 @@ runConnectionPool = runMongoDBConn (ConfirmWrites [u"j" =: True]) withConnectionPool :: (MonadControlIO m, Applicative m) => AppConfig -> (ConnectionPool -> m b) -> m b withConnectionPool conf f = do - (database,host) <- liftIO $ loadConnParams (appEnv conf) + (database,host) <- liftIO $ loadMongoConnParams (appEnv conf) withMongoDBPool (u database) host (connectionPoolSize conf) f - where - -- | The database connection parameters. - -- loadConnParams :: AppEnvironment -> IO (Database, HostName) - loadConnParams env = do - allSettings <- (join $ YAML.decodeFile ("config/mongoDB.yml" :: String)) >>= fromMapping - settings <- lookupMapping (show env) allSettings - database <- lookupScalar "database" settings - host <- lookupScalar "host" settings - return (database, host) + diff --git a/yesod/scaffold/postgresqlConnPool.cg b/yesod/scaffold/postgresqlConnPool.cg index 9cf1129b..e8597e4f 100644 --- a/yesod/scaffold/postgresqlConnPool.cg +++ b/yesod/scaffold/postgresqlConnPool.cg @@ -3,21 +3,8 @@ runConnectionPool = runSqlPool withConnectionPool :: MonadControlIO m => AppConfig -> (ConnectionPool -> m a) -> m a withConnectionPool conf f = do - cs <- liftIO $ loadConnStr (appEnv conf) + cs <- liftIO $ load~upper~ConnStr (appEnv conf) with~upper~Pool cs (connectionPoolSize conf) f - where - -- | The database connection string. The meaning of this string is backend- - -- specific. - loadConnStr :: AppEnvironment -> IO Text - loadConnStr env = do - allSettings <- (join $ YAML.decodeFile ("config/~backendLower~.yml" :: String)) >>= fromMapping - settings <- lookupMapping (show env) allSettings - database <- lookupScalar "database" settings :: IO Text - - connPart <- fmap concat $ (flip mapM) ["user", "password", "host", "port"] $ \key -> do - value <- lookupScalar key settings - return $ [st| #{key}=#{value} |] - return $ [st|#{connPart} dbname=#{database}|] -- Example of making a dynamic configuration static -- use /return $(mkConnStr Production)/ instead of loadConnStr diff --git a/yesod/scaffold/sqliteConnPool.cg b/yesod/scaffold/sqliteConnPool.cg index ba4981f2..e8597e4f 100644 --- a/yesod/scaffold/sqliteConnPool.cg +++ b/yesod/scaffold/sqliteConnPool.cg @@ -3,16 +3,8 @@ runConnectionPool = runSqlPool withConnectionPool :: MonadControlIO m => AppConfig -> (ConnectionPool -> m a) -> m a withConnectionPool conf f = do - cs <- liftIO $ loadConnStr (appEnv conf) + cs <- liftIO $ load~upper~ConnStr (appEnv conf) with~upper~Pool cs (connectionPoolSize conf) f - where - -- | The database connection string. The meaning of this string is backend- - -- specific. - loadConnStr :: AppEnvironment -> IO Text - loadConnStr env = do - allSettings <- (join $ YAML.decodeFile ("config/~backendLower~.yml" :: String)) >>= fromMapping - settings <- lookupMapping (show env) allSettings - lookupScalar "database" settings -- Example of making a dynamic configuration static -- use /return $(mkConnStr Production)/ instead of loadConnStr diff --git a/yesod/scaffold/tiny/Settings.hs.cg b/yesod/scaffold/tiny/Settings.hs.cg index 73e226ad..a36c764b 100644 --- a/yesod/scaffold/tiny/Settings.hs.cg +++ b/yesod/scaffold/tiny/Settings.hs.cg @@ -27,10 +27,7 @@ import Yesod.Widget (addWidget, addCassius, addJulius, addLucius, whamletFile) import Yesod.Settings import Data.Monoid (mempty) import System.Directory (doesFileExist) -~settingsTextImport~ -import Data.Object -import qualified Data.Object.Yaml as YAML -import Control.Monad (join) +import Data.Text (Text) -- | The location of static files on your system. This is a file system -- path. The default value works properly with your scaffolded site. From a9ac0a11b600e8ffcd5e344aebf984f6078ab4e5 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Sun, 11 Sep 2011 14:59:32 -0400 Subject: [PATCH 13/44] Fix up scaffold.sh Use bash -e instead of single pipeline Unregister foobar after testing it --- yesod/test/scaffold.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/yesod/test/scaffold.sh b/yesod/test/scaffold.sh index 34798202..303a9656 100755 --- a/yesod/test/scaffold.sh +++ b/yesod/test/scaffold.sh @@ -1,3 +1,12 @@ -#!/bin/bash -x +#!/bin/bash -ex -rm -rf foobar && runghc main.hs init && cd foobar && cabal install && cabal install -fdevel && cd .. +rm -rf foobar +runghc main.hs init + +( + cd foobar + cabal install + cabal install -fdevel +) + +ghc-pkg unregister foobar From 2aa6f989a6b66405ac532a379841d468b764f64a Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Sun, 11 Sep 2011 15:00:20 -0400 Subject: [PATCH 14/44] Complete rewrite of test/run.sh Output is more useful (IMO). Everything is silenced for the duration of the tests but a count of tests/failures and any errors are output at the end. Pass --verbose to watch stdout during the tests. Logic is also seperated into core setup/runners and individual test definitions. Should be more easily extensible. --- yesod/test/run.sh | 111 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 91 insertions(+), 20 deletions(-) diff --git a/yesod/test/run.sh b/yesod/test/run.sh index 948e7689..3b81e612 100755 --- a/yesod/test/run.sh +++ b/yesod/test/run.sh @@ -1,26 +1,97 @@ -#!/bin/bash -ex +#!/bin/bash -e # -# A wrapper for the shelltest test. Passes along options to shelltest. +# Runs test/scaffold.sh with a variety of inputs. Hides all output +# besides failure details. # -# cabal install shelltestrunner +### -cabal clean && cabal install && cabal sdist +[[ "$1" =~ -v|--verbose ]] && stdout=/dev/stdout || stdout=/dev/null -# I am not that good at shell scripting -# this for loop only operates on 1 file (as per tail -1) -for f in $(ls -1rt dist/*.tar.gz | tail -1) -do - tar -xzvf $f && cd `basename $f .tar.gz` +tmp='/tmp' +pwd="$PWD" - # shelltest is designed to show you the diff of an expected stdout/stdin. We don't care about that. If it compiles, it compiles - # shelltest ../test/scaffold.shelltest --color --diff --all $@ -- --hide-successes +pkg= +dir= - ../test/scaffold.sh < ../test/sqlite-input.txt && - ../test/scaffold.sh < ../test/postgresql-input.txt && - ../test/scaffold.sh < ../test/tiny-input.txt && - ../test/scaffold.sh < ../test/mongodb-input.txt || - (echo "FAILED" && exit 1) - cd .. - rm -r `basename $f .tar.gz` -done -echo "PASSED" +failures=() +n_tested=0 +n_failed=0 + +# runs the function named by $1, silencing stdout and redirecting stderr +# to /tmp/function.errors. failures are tracked to be reported on during +# cleanup +run_test() { # {{{ + local test_function="$*" + + n_tested=$((n_tested+1)) + + if $test_function >"$stdout" 2>"$tmp/$test_function.errors"; then + echo -n '.' + [[ -f "$tmp/$test_function.errors" ]] && rm "$tmp/$test_function.errors" + else + echo -n 'F' + failures+=( "$test_function" ) + n_failed=$((n_failed+1)) + fi +} +# }}} + +# changes back to the original directory, removes the dist file and +# outputs a report of tests and failures +cleanup() { # {{{ + cd "$pwd" + [[ -d "$dir" ]] && rm -r "$dir" + + echo + echo + echo "Tests: $n_tested, Failures: $n_failed." + echo + + [[ $n_failed -eq 0 ]] && return 0 + + for test in ${failures[@]}; do + echo "Failure: $test" + echo 'details:' + echo + + if [[ -f "$tmp/$test.errors" ]]; then + cat "$tmp/$test.errors" + rm "$tmp/$test.errors" + else + echo '' + fi + + echo + done + + return $n_failed +} +# }}} + +# compilation is test #1, sets global variable dir. other tests are run +# from within this directory and it is removed as part of cleanup +test_compile() { + cabal clean + cabal install + cabal sdist + + read -r pkg < <(find dist/ -type f -name '*.tar.gz' | sort -rV) + dir="$(basename "$pkg" .tar.gz)" + + tar -xzf "$pkg" && cd "$dir" +} + +test_sqlite() { ../test/scaffold.sh < ../test/sqlite-input.txt ; } +test_postgresql() { ../test/scaffold.sh < ../test/postgresql-input.txt; } +test_mongodb() { ../test/scaffold.sh < ../test/mongodb-input.txt ; } +test_tiny() { ../test/scaffold.sh < ../test/tiny-input.txt ; } + +echo 'Started' +run_test 'test_compile' +run_test 'test_sqlite' +run_test 'test_postgresql' +run_test 'test_mongodb' +run_test 'test_tiny' +cleanup + +exit $? From b9bc2ee1c5efce1bb2571ecb3f457c8730e1a953 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Sun, 11 Sep 2011 15:12:06 -0400 Subject: [PATCH 15/44] Fix typo in the postgres yaml name --- yesod-core/Yesod/Settings.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yesod-core/Yesod/Settings.hs b/yesod-core/Yesod/Settings.hs index 8e303972..7b924472 100644 --- a/yesod-core/Yesod/Settings.hs +++ b/yesod-core/Yesod/Settings.hs @@ -53,7 +53,7 @@ loadConfig env = do loadPostgresqlConnStr :: AppEnvironment -> IO Text loadPostgresqlConnStr env = do - allSettings <- (join $ YAML.decodeFile ("config/postgesql.yml" :: String)) >>= fromMapping + allSettings <- (join $ YAML.decodeFile ("config/postgresql.yml" :: String)) >>= fromMapping settings <- lookupMapping (show env) allSettings database <- lookupScalar "database" settings :: IO Text From 41f17183877d5700e8407a302a465d9bbb7bf970 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Sun, 11 Sep 2011 16:52:54 -0400 Subject: [PATCH 16/44] Provide a default for withDevelAppPort --- yesod-core/Yesod/Main.hs | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/yesod-core/Yesod/Main.hs b/yesod-core/Yesod/Main.hs index bba1fd3e..98b13de4 100644 --- a/yesod-core/Yesod/Main.hs +++ b/yesod-core/Yesod/Main.hs @@ -3,12 +3,15 @@ module Yesod.Main ( defaultMain , fromArgs , fromArgsWith + , defaultDevelApp + , defaultDevelAppWith ) where -import Yesod.Logger (Logger, makeLogger) +import Yesod.Logger (Logger, makeLogger, logString, logLazyText, flushLogger) import Yesod.Settings (AppEnvironment(..), AppConfig(..), loadConfig) import Network.Wai (Application) import Network.Wai.Handler.Warp (run) +import Network.Wai.Middleware.Debug (debugHandle) import System.Console.CmdArgs hiding (args) import Data.Char (toUpper, toLower) @@ -72,3 +75,33 @@ fromArgsWith load = do capitalize [] = [] capitalize (x:xs) = toUpper x : map toLower xs + +-- | A default argument for use with yesod devel with debug logging +-- enabled. Uses @'Yesod.Settings.loadConfig'@ for the @'Development'@ +-- environment. +-- +-- > -- Application.hs +-- > +-- > withDevelAppPort :: Dynamic +-- > withDevelAppPort = toDyn $ defaultDevelApp withMySite +-- +defaultDevelApp :: (AppConfig -> Logger -> (Application -> IO ()) -> IO ()) + -> ((Int, Application) -> IO ()) + -> IO () +defaultDevelApp = defaultDevelAppWith loadConfig + +-- | Same, but allows one to provide their own cust @'loadConfig'@ +defaultDevelAppWith :: (AppEnvironment -> IO AppConfig) + -> (AppConfig -> Logger -> (Application -> IO ()) -> IO ()) + -> ((Int, Application) -> IO ()) + -> IO () +defaultDevelAppWith load withSite f = do + conf <- load Development + logger <- makeLogger + let p = appPort conf + logString logger $ "Devel application launched, listening on port " ++ show p + withSite conf logger $ \app -> f (p, debugHandle (logHandle logger) app) + flushLogger logger + + where + logHandle logger msg = logLazyText logger msg >> flushLogger logger From f06f11ff2616da1ce43d70c21fb85445a68f9d7c Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Sun, 11 Sep 2011 17:15:35 -0400 Subject: [PATCH 17/44] Simplify scaffold futher by using defaultDevelApp --- yesod/scaffold/Application.hs.cg | 17 ++--------------- yesod/scaffold/tiny/Application.hs.cg | 18 +++--------------- 2 files changed, 5 insertions(+), 30 deletions(-) diff --git a/yesod/scaffold/Application.hs.cg b/yesod/scaffold/Application.hs.cg index 8688fe02..02d95819 100644 --- a/yesod/scaffold/Application.hs.cg +++ b/yesod/scaffold/Application.hs.cg @@ -12,11 +12,10 @@ import Foundation import Settings import Settings.StaticFiles (static) import Yesod.Auth -import Yesod.Logger (makeLogger, flushLogger, Logger, logString, logLazyText) +import Yesod.Logger (Logger) import Database.Persist.~importGenericDB~ import Data.ByteString (ByteString) import Data.Dynamic (Dynamic, toDyn) -import Network.Wai.Middleware.Debug (debugHandle) #ifndef WINDOWS import qualified System.Posix.Signals as Signal @@ -63,16 +62,4 @@ with~sitearg~ conf logger f = do -- for yesod devel withDevelAppPort :: Dynamic -withDevelAppPort = - toDyn go - where - go :: ((Int, Application) -> IO ()) -> IO () - go f = do - conf <- loadConfig Development - let port = appPort conf - logger <- makeLogger - logString logger $ "Devel application launched, listening on port " ++ show port - with~sitearg~ conf logger $ \app -> f (port, debugHandle (logHandle logger) app) - flushLogger logger - where - logHandle logger msg = logLazyText logger msg >> flushLogger logger +withDevelAppPort = toDyn $ defaultDevelApp with~sitearg~ diff --git a/yesod/scaffold/tiny/Application.hs.cg b/yesod/scaffold/tiny/Application.hs.cg index e4e2aaef..77c73898 100644 --- a/yesod/scaffold/tiny/Application.hs.cg +++ b/yesod/scaffold/tiny/Application.hs.cg @@ -12,11 +12,11 @@ import Foundation import Settings import Yesod.Static import Yesod.Settings -import Yesod.Logger (makeLogger, flushLogger, Logger, logLazyText, logString) +import Yesod.Main (defaultDevelApp) +import Yesod.Logger (Logger) import Data.ByteString (ByteString) import Network.Wai (Application) import Data.Dynamic (Dynamic, toDyn) -import Network.Wai.Middleware.Debug (debugHandle) -- Import all relevant handler modules here. import Handler.Root @@ -50,16 +50,4 @@ with~sitearg~ conf logger f = do -- for yesod devel withDevelAppPort :: Dynamic -withDevelAppPort = - toDyn go - where - go :: ((Int, Application) -> IO ()) -> IO () - go f = do - conf <- loadConfig Development - let port = appPort conf - logger <- makeLogger - logString logger $ "Devel application launched, listening on port " ++ show port - with~sitearg~ conf logger $ \app -> f (port, debugHandle (logHandle logger) app) - flushLogger logger - where - logHandle logger msg = logLazyText logger msg >> flushLogger logger +withDevelAppPort = toDyn $ defaultDevelApp with~sitearg~ From 900e9476f3bd88ab10b445e7c5c374c2c0398c2c Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Tue, 13 Sep 2011 21:28:33 -0400 Subject: [PATCH 18/44] Move Settings/Main out of -core --- yesod-core/yesod-core.cabal | 7 ------- {yesod-core => yesod}/Yesod/Main.hs | 0 {yesod-core => yesod}/Yesod/Settings.hs | 0 yesod/yesod.cabal | 6 ++++++ 4 files changed, 6 insertions(+), 7 deletions(-) rename {yesod-core => yesod}/Yesod/Main.hs (100%) rename {yesod-core => yesod}/Yesod/Settings.hs (100%) diff --git a/yesod-core/yesod-core.cabal b/yesod-core/yesod-core.cabal index ce7a8c0c..1b520dc4 100644 --- a/yesod-core/yesod-core.cabal +++ b/yesod-core/yesod-core.cabal @@ -38,7 +38,6 @@ library , shakespeare >= 0.10 && < 0.11 , shakespeare-js >= 0.10 && < 0.11 , shakespeare-css >= 0.10 && < 0.11 - , shakespeare-text >= 0.10 && < 0.11 , blaze-builder >= 0.2.1 && < 0.4 , transformers >= 0.2 && < 0.3 , clientsession >= 0.7.2 && < 0.8 @@ -55,10 +54,6 @@ library , case-insensitive >= 0.2 && < 0.4 , parsec >= 2 && < 3.2 , directory >= 1 && < 1.2 - , cmdargs >= 0.8 && < 0.9 - , data-object >= 0.3 && < 0.4 - , data-object-yaml >= 0.3 && < 0.4 - , warp >= 0.4 && < 0.5 -- for logger. Probably logger should be a separate package , strict-concurrency >= 0.2.4 && < 0.2.5 @@ -70,8 +65,6 @@ library Yesod.Request Yesod.Widget Yesod.Message - Yesod.Settings - Yesod.Main other-modules: Yesod.Internal Yesod.Internal.Core Yesod.Internal.Session diff --git a/yesod-core/Yesod/Main.hs b/yesod/Yesod/Main.hs similarity index 100% rename from yesod-core/Yesod/Main.hs rename to yesod/Yesod/Main.hs diff --git a/yesod-core/Yesod/Settings.hs b/yesod/Yesod/Settings.hs similarity index 100% rename from yesod-core/Yesod/Settings.hs rename to yesod/Yesod/Settings.hs diff --git a/yesod/yesod.cabal b/yesod/yesod.cabal index 94a340d4..37c0634a 100644 --- a/yesod/yesod.cabal +++ b/yesod/yesod.cabal @@ -74,9 +74,15 @@ library , hamlet >= 0.10 && < 0.11 , shakespeare-js >= 0.10 && < 0.11 , shakespeare-css >= 0.10 && < 0.11 + , shakespeare-text >= 0.10 && < 0.11 , warp >= 0.4 && < 0.5 , blaze-html >= 0.4 && < 0.5 + , cmdargs >= 0.8 && < 0.9 + , data-object >= 0.3 && < 0.4 + , data-object-yaml >= 0.3 && < 0.4 exposed-modules: Yesod + Yesod.Settings + Yesod.Main ghc-options: -Wall executable yesod From 2d0d70fd60c93f6aa29ddadd9e40d83670f3a660 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Tue, 13 Sep 2011 21:55:11 -0400 Subject: [PATCH 19/44] bring comments back in --- yesod/Yesod/Settings.hs | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/yesod/Yesod/Settings.hs b/yesod/Yesod/Settings.hs index 7b924472..148b081a 100644 --- a/yesod/Yesod/Settings.hs +++ b/yesod/Yesod/Settings.hs @@ -23,13 +23,41 @@ data AppEnvironment = Development | Production deriving (Eq, Show, Read, Enum, Bounded) +-- | Dynamic per-environment configuration which can be loaded at +-- run-time negating the need to recompile between environments. data AppConfig = AppConfig - { appEnv :: AppEnvironment - , appPort :: Int + { appEnv :: AppEnvironment + , appPort :: Int + + -- | Your application will keep a connection pool and take + -- connections from there as necessary instead of continually + -- creating new connections. This value gives the maximum number + -- of connections to be open at a given time. If your application + -- requests a connection when all connections are in use, that + -- request will fail. Try to choose a number that will work well + -- with the system resources available to you while providing + -- enough connections for your expected load. + -- + -- Connections are returned to the pool as quickly as possible by + -- Yesod to avoid resource exhaustion. A connection is only + -- considered in use while within a call to runDB. , connectionPoolSize :: Int - , appRoot :: Text + + -- | The base URL for your application. This will usually be + -- different for development and production. Yesod automatically + -- constructs URLs for you, so this value must be accurate to + -- create valid links. + -- + -- If your domain name was "yesod.com", you would probably want it + -- to be: + -- + -- > "http://yesod.com" + -- + , appRoot :: Text } deriving (Show) +-- | Load an @'AppConfig'@ from a YAML-formatted file located at +-- @config\/settings.yml@. loadConfig :: AppEnvironment -> IO AppConfig loadConfig env = do allSettings <- (join $ YAML.decodeFile ("config/settings.yml" :: String)) >>= fromMapping @@ -51,6 +79,8 @@ loadConfig env = do Production -> "" _ -> ":" ++ show p +-- | Load Postgresql settings from a YAML-formatted file located at +-- @config\/postgresql.yml@. loadPostgresqlConnStr :: AppEnvironment -> IO Text loadPostgresqlConnStr env = do allSettings <- (join $ YAML.decodeFile ("config/postgresql.yml" :: String)) >>= fromMapping @@ -62,13 +92,16 @@ loadPostgresqlConnStr env = do return $ [st| #{key}=#{value} |] return $ [st|#{connPart} dbname=#{database}|] +-- | Load Sqlite settings from a YAML-formatted file located at +-- @config\/sqlite.yml@. loadSqliteConnStr :: AppEnvironment -> IO Text loadSqliteConnStr env = do allSettings <- (join $ YAML.decodeFile ("config/sqlite.yml" :: String)) >>= fromMapping settings <- lookupMapping (show env) allSettings lookupScalar "database" settings --- note: no type signature to avoid Persistent.MongoDB dep +-- note: no type signature to avoid an extra Persistent.MongoDB dep for +-- those that don't need it --loadMongoConnParams :: AppEnvironment -> IO (Database, HostName) loadMongoConnParams env = do allSettings <- (join $ YAML.decodeFile ("config/mongoDB.yml" :: String)) >>= fromMapping From f41029fd4cc53454f353fa56855e9c5ef080751c Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Tue, 13 Sep 2011 23:07:27 -0400 Subject: [PATCH 20/44] Add yesod dep to tiny scaffold --- yesod/scaffold/tiny/project.cabal.cg | 1 + 1 file changed, 1 insertion(+) diff --git a/yesod/scaffold/tiny/project.cabal.cg b/yesod/scaffold/tiny/project.cabal.cg index 527f265b..6e5c3cc5 100644 --- a/yesod/scaffold/tiny/project.cabal.cg +++ b/yesod/scaffold/tiny/project.cabal.cg @@ -46,6 +46,7 @@ executable ~project~ hs-source-dirs: . build-depends: base >= 4 && < 5 + , yesod >= 0.9 && < 0.10 , yesod-core >= 0.9 && < 0.10 , yesod-static , clientsession From cfb6e1e24a6eb5f8ebafc6d1c1c038b3278ce98f Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Mon, 19 Sep 2011 15:12:19 -0400 Subject: [PATCH 21/44] Move bulk of Settings/Main out of -core Trim settings to only the AppConfig def and an AppEnv typeclass, rename to Config but leave in -core --- .../Settings.hs => yesod-core/Yesod/Config.hs | 40 +++---- yesod-core/Yesod/Core.hs | 2 + yesod-core/yesod-core.cabal | 4 + yesod/Yesod/Main.hs | 107 ------------------ 4 files changed, 19 insertions(+), 134 deletions(-) rename yesod/Yesod/Settings.hs => yesod-core/Yesod/Config.hs (70%) delete mode 100644 yesod/Yesod/Main.hs diff --git a/yesod/Yesod/Settings.hs b/yesod-core/Yesod/Config.hs similarity index 70% rename from yesod/Yesod/Settings.hs rename to yesod-core/Yesod/Config.hs index 148b081a..2fccc1e6 100644 --- a/yesod/Yesod/Settings.hs +++ b/yesod-core/Yesod/Config.hs @@ -1,7 +1,7 @@ {-# OPTIONS -fno-warn-missing-signatures #-} {-# LANGUAGE QuasiQuotes #-} -module Yesod.Settings - ( AppEnvironment(..) +module Yesod.Config + ( AppEnv(..) , AppConfig(..) , loadConfig , loadPostgresqlConnStr @@ -17,30 +17,16 @@ import Text.Shakespeare.Text (st) import qualified Data.Object.Yaml as YAML import qualified Data.Text as T -data AppEnvironment = Development - | Test - | Staging - | Production - deriving (Eq, Show, Read, Enum, Bounded) +class AppEnv e where + displayPort :: e -> Bool -- | Dynamic per-environment configuration which can be loaded at -- run-time negating the need to recompile between environments. -data AppConfig = AppConfig - { appEnv :: AppEnvironment +data AppConfig e = AppConfig + { appEnv :: e , appPort :: Int - -- | Your application will keep a connection pool and take - -- connections from there as necessary instead of continually - -- creating new connections. This value gives the maximum number - -- of connections to be open at a given time. If your application - -- requests a connection when all connections are in use, that - -- request will fail. Try to choose a number that will work well - -- with the system resources available to you while providing - -- enough connections for your expected load. - -- - -- Connections are returned to the pool as quickly as possible by - -- Yesod to avoid resource exhaustion. A connection is only - -- considered in use while within a call to runDB. + -- TODO: put this in db configs , connectionPoolSize :: Int -- | The base URL for your application. This will usually be @@ -56,9 +42,10 @@ data AppConfig = AppConfig , appRoot :: Text } deriving (Show) + -- | Load an @'AppConfig'@ from a YAML-formatted file located at -- @config\/settings.yml@. -loadConfig :: AppEnvironment -> IO AppConfig +loadConfig :: AppEnv e => e -> IO (AppConfig e) loadConfig env = do allSettings <- (join $ YAML.decodeFile ("config/settings.yml" :: String)) >>= fromMapping settings <- lookupMapping (show env) allSettings @@ -75,13 +62,12 @@ loadConfig env = do where addPort :: Int -> String - addPort p = case env of - Production -> "" - _ -> ":" ++ show p + addPort p = if displayPort env + then ":" ++ show p else "" -- | Load Postgresql settings from a YAML-formatted file located at -- @config\/postgresql.yml@. -loadPostgresqlConnStr :: AppEnvironment -> IO Text +loadPostgresqlConnStr :: Show e => e -> IO Text loadPostgresqlConnStr env = do allSettings <- (join $ YAML.decodeFile ("config/postgresql.yml" :: String)) >>= fromMapping settings <- lookupMapping (show env) allSettings @@ -94,7 +80,7 @@ loadPostgresqlConnStr env = do -- | Load Sqlite settings from a YAML-formatted file located at -- @config\/sqlite.yml@. -loadSqliteConnStr :: AppEnvironment -> IO Text +loadSqliteConnStr :: Show e => e -> IO Text loadSqliteConnStr env = do allSettings <- (join $ YAML.decodeFile ("config/sqlite.yml" :: String)) >>= fromMapping settings <- lookupMapping (show env) allSettings diff --git a/yesod-core/Yesod/Core.hs b/yesod-core/Yesod/Core.hs index 9f137991..2e395856 100644 --- a/yesod-core/Yesod/Core.hs +++ b/yesod-core/Yesod/Core.hs @@ -33,6 +33,7 @@ module Yesod.Core , module Yesod.Request , module Yesod.Widget , module Yesod.Message + , module Yesod.Config ) where import Yesod.Internal.Core @@ -42,6 +43,7 @@ import Yesod.Handler import Yesod.Request import Yesod.Widget import Yesod.Message +import Yesod.Config import Language.Haskell.TH.Syntax import Data.Text (Text) diff --git a/yesod-core/yesod-core.cabal b/yesod-core/yesod-core.cabal index 1b520dc4..a499a2d3 100644 --- a/yesod-core/yesod-core.cabal +++ b/yesod-core/yesod-core.cabal @@ -38,6 +38,7 @@ library , shakespeare >= 0.10 && < 0.11 , shakespeare-js >= 0.10 && < 0.11 , shakespeare-css >= 0.10 && < 0.11 + , shakespeare-text >= 0.10 && < 0.11 , blaze-builder >= 0.2.1 && < 0.4 , transformers >= 0.2 && < 0.3 , clientsession >= 0.7.2 && < 0.8 @@ -54,6 +55,8 @@ library , case-insensitive >= 0.2 && < 0.4 , parsec >= 2 && < 3.2 , directory >= 1 && < 1.2 + , data-object >= 0.3 && < 0.4 + , data-object-yaml >= 0.3 && < 0.4 -- for logger. Probably logger should be a separate package , strict-concurrency >= 0.2.4 && < 0.2.5 @@ -65,6 +68,7 @@ library Yesod.Request Yesod.Widget Yesod.Message + Yesod.Config other-modules: Yesod.Internal Yesod.Internal.Core Yesod.Internal.Session diff --git a/yesod/Yesod/Main.hs b/yesod/Yesod/Main.hs deleted file mode 100644 index 98b13de4..00000000 --- a/yesod/Yesod/Main.hs +++ /dev/null @@ -1,107 +0,0 @@ -{-# LANGUAGE DeriveDataTypeable #-} -module Yesod.Main - ( defaultMain - , fromArgs - , fromArgsWith - , defaultDevelApp - , defaultDevelAppWith - ) where - -import Yesod.Logger (Logger, makeLogger, logString, logLazyText, flushLogger) -import Yesod.Settings (AppEnvironment(..), AppConfig(..), loadConfig) -import Network.Wai (Application) -import Network.Wai.Handler.Warp (run) -import Network.Wai.Middleware.Debug (debugHandle) -import System.Console.CmdArgs hiding (args) -import Data.Char (toUpper, toLower) - -data ArgConfig = ArgConfig - { environment :: String - , port :: Int - } deriving (Show, Data, Typeable) - --- | Load an @'AppConfig'@ using the provided function, then start your --- app via Warp on the configured port. --- --- > -- main.hs --- > import Application (withMySite) --- > import Yesod.Main (defaultMain, fromArgs) --- > --- > main :: IO () --- > main = defaultMain fromArgs withMySite --- -defaultMain :: IO AppConfig -> (AppConfig -> Logger -> (Application -> IO ()) -> IO ()) -> IO () -defaultMain load withSite = do - config <- load - logger <- makeLogger - withSite config logger $ run (appPort config) - --- | Call the @'Yesod.Settings.loadConfig'@ function for the environment --- passed on the commandline (or the default, \"development\") and --- override the port if passed. -fromArgs :: IO AppConfig -fromArgs = fromArgsWith loadConfig - --- | Same, but allows one to provide their own custom @'loadConfig'@ -fromArgsWith :: (AppEnvironment -> IO AppConfig) -> IO AppConfig -fromArgsWith load = do - args <- cmdArgs argConfig - - let env = read - $ capitalize - $ if environment args /= "" - then environment args - else "development" - - config <- load env - - return $ if port args /= 0 - then config { appPort = port args } - else config - - where - argConfig :: ArgConfig - argConfig = ArgConfig - { environment = def - &= help ("application environment, one of: " ++ (foldl1 (\a b -> a ++ ", " ++ b) environments)) - &= typ "ENVIRONMENT" - , port = def - &= help "the port to listen on" - &= typ "PORT" - } - - environments :: [String] - environments = map ((map toLower) . show) ([minBound..maxBound] :: [AppEnvironment]) - - capitalize [] = [] - capitalize (x:xs) = toUpper x : map toLower xs - --- | A default argument for use with yesod devel with debug logging --- enabled. Uses @'Yesod.Settings.loadConfig'@ for the @'Development'@ --- environment. --- --- > -- Application.hs --- > --- > withDevelAppPort :: Dynamic --- > withDevelAppPort = toDyn $ defaultDevelApp withMySite --- -defaultDevelApp :: (AppConfig -> Logger -> (Application -> IO ()) -> IO ()) - -> ((Int, Application) -> IO ()) - -> IO () -defaultDevelApp = defaultDevelAppWith loadConfig - --- | Same, but allows one to provide their own cust @'loadConfig'@ -defaultDevelAppWith :: (AppEnvironment -> IO AppConfig) - -> (AppConfig -> Logger -> (Application -> IO ()) -> IO ()) - -> ((Int, Application) -> IO ()) - -> IO () -defaultDevelAppWith load withSite f = do - conf <- load Development - logger <- makeLogger - let p = appPort conf - logString logger $ "Devel application launched, listening on port " ++ show p - withSite conf logger $ \app -> f (p, debugHandle (logHandle logger) app) - flushLogger logger - - where - logHandle logger msg = logLazyText logger msg >> flushLogger logger From 4f52f22a2ccb89f87257ef66d0871066012d587b Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Mon, 19 Sep 2011 15:14:41 -0400 Subject: [PATCH 22/44] Put yesod module back as it was --- yesod/Yesod.hs | 4 ---- yesod/yesod.cabal | 6 ------ 2 files changed, 10 deletions(-) diff --git a/yesod/Yesod.hs b/yesod/Yesod.hs index 6fc6ee98..7724a345 100644 --- a/yesod/Yesod.hs +++ b/yesod/Yesod.hs @@ -7,8 +7,6 @@ module Yesod , module Yesod.Form , module Yesod.Json , module Yesod.Persist - , module Yesod.Settings - , module Yesod.Main -- * Running your application , warp , warpDebug @@ -48,8 +46,6 @@ import Text.Julius import Yesod.Form import Yesod.Json import Yesod.Persist -import Yesod.Settings -import Yesod.Main import Network.Wai (Application) import Network.Wai.Middleware.Debug import Control.Monad.Trans.Class (lift) diff --git a/yesod/yesod.cabal b/yesod/yesod.cabal index 37c0634a..94a340d4 100644 --- a/yesod/yesod.cabal +++ b/yesod/yesod.cabal @@ -74,15 +74,9 @@ library , hamlet >= 0.10 && < 0.11 , shakespeare-js >= 0.10 && < 0.11 , shakespeare-css >= 0.10 && < 0.11 - , shakespeare-text >= 0.10 && < 0.11 , warp >= 0.4 && < 0.5 , blaze-html >= 0.4 && < 0.5 - , cmdargs >= 0.8 && < 0.9 - , data-object >= 0.3 && < 0.4 - , data-object-yaml >= 0.3 && < 0.4 exposed-modules: Yesod - Yesod.Settings - Yesod.Main ghc-options: -Wall executable yesod From b031702a9bec56c9a1bfba6275ce867a260f182b Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Mon, 19 Sep 2011 15:15:04 -0400 Subject: [PATCH 23/44] Add yesod-default Still needs the cabal, Setup, etc etc but at least it compiles and is somewhat extensible. --- yesod-default/Yesod/Default/Config.hs | 76 +++++++++++++++++++++++++++ yesod-default/Yesod/Default/Main.hs | 65 +++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 yesod-default/Yesod/Default/Config.hs create mode 100644 yesod-default/Yesod/Default/Main.hs diff --git a/yesod-default/Yesod/Default/Config.hs b/yesod-default/Yesod/Default/Config.hs new file mode 100644 index 00000000..7f92b3b8 --- /dev/null +++ b/yesod-default/Yesod/Default/Config.hs @@ -0,0 +1,76 @@ +{-# LANGUAGE DeriveDataTypeable #-} +module Yesod.Default.Config + ( DefaultEnv(..) + , ArgConfig(..) + , defaultArgConfig + , fromArgs + , fromArgsWith + , loadDevelopmentConfig + + -- reexport + , module Yesod.Config + ) where + +import Yesod.Config +import Data.Char (toUpper, toLower) +import System.Console.CmdArgs hiding (args) + +-- | A yesod-provided @'AppEnv'@, allows for Development, Testing, and +-- Production environments +data DefaultEnv = Development + | Testing + | Production deriving (Read, Show, Enum, Bounded) + +instance AppEnv DefaultEnv where + displayPort Production = False + displayPort _ = True + +-- | Setup commandline arguments for environment and port +data ArgConfig = ArgConfig + { environment :: String + , port :: Int + } deriving (Show, Data, Typeable) + +-- | A default @'ArgConfig'@ if using the provided @'DefaultEnv'@ type. +defaultArgConfig :: ArgConfig +defaultArgConfig = + ArgConfig + { environment = def + &= opt "development" + &= help ("application environment, one of: " ++ environments) + &= typ "ENVIRONMENT" + , port = def + &= help "the port to listen on" + &= typ "PORT" + } + + where + environments :: String + environments = foldl1 (\a b -> a ++ ", " ++ b) + . map ((map toLower) . show) + $ ([minBound..maxBound] :: [DefaultEnv]) + +-- | Load an @'AppConfig'@ using the @'DefaultEnv'@ environments from +-- commandline arguments. +fromArgs :: IO (AppConfig DefaultEnv) +fromArgs = fromArgsWith defaultArgConfig + +fromArgsWith :: AppEnv e => ArgConfig -> IO (AppConfig e) +fromArgsWith argConfig = do + args <- cmdArgs argConfig + + let env = read $ capitalize $ environment args + + config <- loadConfig env + + return $ if port args /= 0 + then config { appPort = port args } + else config + + where + capitalize [] = [] + capitalize (x:xs) = toUpper x : map toLower xs + +-- | Load your development config (when using @'DefaultEnv'@) +loadDevelopmentConfig :: IO (AppConfig DefaultEnv) +loadDevelopmentConfig = loadConfig Development diff --git a/yesod-default/Yesod/Default/Main.hs b/yesod-default/Yesod/Default/Main.hs new file mode 100644 index 00000000..9a1e2e45 --- /dev/null +++ b/yesod-default/Yesod/Default/Main.hs @@ -0,0 +1,65 @@ +{-# LANGUAGE DeriveDataTypeable #-} +module Yesod.Default.Main + ( defaultMain + , defaultDevelApp + , defaultDevelAppWith + ) where + +import Yesod.Default.Config +import Yesod.Logger (Logger, makeLogger, logString, logLazyText, flushLogger) +import Network.Wai (Application) +import Network.Wai.Handler.Warp (run) +import Network.Wai.Middleware.Debug (debugHandle) + +-- | Run your app, taking environment and port settings from the +-- commandline. +-- +-- Use @'fromArgs'@ when using the provided @'DefaultEnv'@ type, or +-- @'fromArgsWith'@ when using a custom type +-- +-- > main :: IO () +-- > main = defaultMain fromArgs withMySite +-- +-- or +-- +-- > main :: IO () +-- > main = defaultMain (fromArgsWith customArgConfig) withMySite +-- +defaultMain :: AppEnv e => IO (AppConfig e) -> (AppConfig e -> Logger -> (Application -> IO ()) -> IO ()) -> IO () +defaultMain load withSite = do + config <- load + logger <- makeLogger + withSite config logger $ run (appPort config) + +-- | Run your development app using the provided @'DefaultEnv'@ type +-- +-- > withDevelAppPort :: Dynamic +-- > withDevelAppPort = toDyn $ defaultDevelApp withMySite +-- +defaultDevelApp :: (AppConfig DefaultEnv -> Logger -> (Application -> IO ()) -> IO ()) + -> ((Int, Application) -> IO ()) + -> IO () +defaultDevelApp = defaultDevelAppWith loadDevelopmentConfig + +-- | Run your development app using a custom environment type and loader +-- function +-- +-- > withDevelAppPort :: Dynamic +-- > withDevelAppPort = toDyn $ (defaultDevelAppWith customLoadAppConfig) withMySite +-- +defaultDevelAppWith :: AppEnv e + -- | A means to load your development @'AppConfig'@ + => IO (AppConfig e) + -- | Your @withMySite@ function + -> (AppConfig e -> Logger -> (Application -> IO ()) -> IO ()) + -> ((Int, Application) -> IO ()) -> IO () +defaultDevelAppWith load withSite f = do + conf <- load + logger <- makeLogger + let p = appPort conf + logString logger $ "Devel application launched, listening on port " ++ show p + withSite conf logger $ \app -> f (p, debugHandle (logHandle logger) app) + flushLogger logger + + where + logHandle logger msg = logLazyText logger msg >> flushLogger logger From aef2992005a53a8a49c1e2bb5c266072bbdc9d73 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Mon, 19 Sep 2011 16:10:46 -0400 Subject: [PATCH 24/44] fix haddock parse --- yesod-default/Yesod/Default/Main.hs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/yesod-default/Yesod/Default/Main.hs b/yesod-default/Yesod/Default/Main.hs index 9a1e2e45..2ac9ee69 100644 --- a/yesod-default/Yesod/Default/Main.hs +++ b/yesod-default/Yesod/Default/Main.hs @@ -48,10 +48,8 @@ defaultDevelApp = defaultDevelAppWith loadDevelopmentConfig -- > withDevelAppPort = toDyn $ (defaultDevelAppWith customLoadAppConfig) withMySite -- defaultDevelAppWith :: AppEnv e - -- | A means to load your development @'AppConfig'@ - => IO (AppConfig e) - -- | Your @withMySite@ function - -> (AppConfig e -> Logger -> (Application -> IO ()) -> IO ()) + => IO (AppConfig e) -- ^ A means to load your development @'AppConfig'@ + -> (AppConfig e -> Logger -> (Application -> IO ()) -> IO ()) -- ^ Your @withMySite@ function -> ((Int, Application) -> IO ()) -> IO () defaultDevelAppWith load withSite f = do conf <- load From 0563a2d5e47a650a774c2b7c084bf91c1205f6b9 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Mon, 19 Sep 2011 16:10:59 -0400 Subject: [PATCH 25/44] Make yesod-default installable --- yesod-default/LICENSE | 25 +++++++++++++++++++++++++ yesod-default/README | 0 yesod-default/Setup.lhs | 7 +++++++ yesod-default/yesod-default.cabal | 31 +++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+) create mode 100644 yesod-default/LICENSE create mode 100644 yesod-default/README create mode 100755 yesod-default/Setup.lhs create mode 100644 yesod-default/yesod-default.cabal diff --git a/yesod-default/LICENSE b/yesod-default/LICENSE new file mode 100644 index 00000000..8643e5d8 --- /dev/null +++ b/yesod-default/LICENSE @@ -0,0 +1,25 @@ +The following license covers this documentation, and the source code, except +where otherwise indicated. + +Copyright 2010, Michael Snoyman. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/yesod-default/README b/yesod-default/README new file mode 100644 index 00000000..e69de29b diff --git a/yesod-default/Setup.lhs b/yesod-default/Setup.lhs new file mode 100755 index 00000000..06e2708f --- /dev/null +++ b/yesod-default/Setup.lhs @@ -0,0 +1,7 @@ +#!/usr/bin/env runhaskell + +> module Main where +> import Distribution.Simple + +> main :: IO () +> main = defaultMain diff --git a/yesod-default/yesod-default.cabal b/yesod-default/yesod-default.cabal new file mode 100644 index 00000000..306b6fb1 --- /dev/null +++ b/yesod-default/yesod-default.cabal @@ -0,0 +1,31 @@ +name: yesod-default +version: 0.3.1 +license: BSD3 +license-file: LICENSE +author: Patrick Brisbin +maintainer: Patrick Brisbin +synopsis: Default config and main functions for your yesod application +category: Web, Yesod +stability: Stable +cabal-version: >= 1.6 +build-type: Simple +homepage: http://www.yesodweb.com/ +description: Convenient wrappers for your the configuration and + execution of your yesod application + +library + build-depends: base >= 4 && < 5 + , yesod-core >= 0.9 && < 0.10 + , cmdargs >= 0.8 && < 0.9 + , warp >= 0.4 && < 0.5 + , wai >= 0.4 && < 0.5 + , wai-extra >= 0.4 && < 0.5 + + exposed-modules: Yesod.Default.Config + , Yesod.Default.Main + + ghc-options: -Wall + +source-repository head + type: git + location: git://github.com/yesodweb/yesod.git From ca55a891c81a9248f2dd0cb5df7a2afb3a1f1fb5 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Tue, 20 Sep 2011 22:55:31 -0400 Subject: [PATCH 26/44] Rewrite AppConfig loader * Remove AppEnv * Add logic to set approort smartly in most cases * Refactor YAML parser * Update yesod-default to match There's still much todo... --- yesod-core/Yesod/Config.hs | 137 ++++++++++++++------------ yesod-default/Yesod/Default/Config.hs | 6 +- yesod-default/Yesod/Default/Main.hs | 4 +- 3 files changed, 79 insertions(+), 68 deletions(-) diff --git a/yesod-core/Yesod/Config.hs b/yesod-core/Yesod/Config.hs index 2fccc1e6..5aa1a9a7 100644 --- a/yesod-core/Yesod/Config.hs +++ b/yesod-core/Yesod/Config.hs @@ -1,8 +1,7 @@ {-# OPTIONS -fno-warn-missing-signatures #-} {-# LANGUAGE QuasiQuotes #-} module Yesod.Config - ( AppEnv(..) - , AppConfig(..) + ( AppConfig(..) , loadConfig , loadPostgresqlConnStr , loadSqliteConnStr @@ -10,88 +9,104 @@ module Yesod.Config ) where import Control.Monad (join) +import Data.Maybe (fromMaybe) import Data.Object +import Data.Object.Yaml import Data.Text (Text) -import Text.Shakespeare.Text (st) +--import Text.Shakespeare.Text (st) -import qualified Data.Object.Yaml as YAML import qualified Data.Text as T -class AppEnv e where - displayPort :: e -> Bool - -- | Dynamic per-environment configuration which can be loaded at -- run-time negating the need to recompile between environments. data AppConfig e = AppConfig { appEnv :: e , appPort :: Int - - -- TODO: put this in db configs - , connectionPoolSize :: Int - - -- | The base URL for your application. This will usually be - -- different for development and production. Yesod automatically - -- constructs URLs for you, so this value must be accurate to - -- create valid links. - -- - -- If your domain name was "yesod.com", you would probably want it - -- to be: - -- - -- > "http://yesod.com" - -- , appRoot :: Text } deriving (Show) +-- | Dynamic per-environment database configuration which can be loaded +-- at run-time +data DbConfig = PostgresConf String String Int -- ^ Connection string, Database, Pool size + | SqliteConf String Int -- ^ Database, Pool size + | MongoConf (String,String) Int -- ^ (Database,Host), Pool size -- | Load an @'AppConfig'@ from a YAML-formatted file located at --- @config\/settings.yml@. -loadConfig :: AppEnv e => e -> IO (AppConfig e) -loadConfig env = do - allSettings <- (join $ YAML.decodeFile ("config/settings.yml" :: String)) >>= fromMapping - settings <- lookupMapping (show env) allSettings - hostS <- lookupScalar "host" settings - port <- fmap read $ lookupScalar "port" settings - connectionPoolSizeS <- lookupScalar "connectionPoolSize" settings +-- @config\/settings.yml@. @'show'@ will be called on the first +-- parameter to determine which environment block to load +loadConfig :: Show e => e -> IO (AppConfig e) +loadConfig env = withYamlEnvironment "config/settings.yml" env $ \e -> do + let mssl = lookupScalar "ssl" e + let mhost = lookupScalar "host" e + let mport = lookupScalar "port" e + let mapproot = lookupScalar "approot" e + + -- set some default arguments + let ssl = toBool $ fromMaybe "false" mssl + port <- safeRead $ fromMaybe (if ssl then "443" else "80") mport + + approot <- case (mhost, mapproot) of + (_ , Just ar) -> Just ar + (Just host, _ ) -> Just $ (if ssl then "http://" else "https://") ++ host ++ (addPort ssl port) + _ -> Nothing return $ AppConfig { appEnv = env , appPort = port - , appRoot = T.pack $ hostS ++ addPort port - , connectionPoolSize = read connectionPoolSizeS + , appRoot = T.pack approot } where - addPort :: Int -> String - addPort p = if displayPort env - then ":" ++ show p else "" + toBool :: String -> Bool + toBool = (`elem` ["true", "TRUE", "yes", "YES", "Y", "1"]) --- | Load Postgresql settings from a YAML-formatted file located at --- @config\/postgresql.yml@. -loadPostgresqlConnStr :: Show e => e -> IO Text -loadPostgresqlConnStr env = do - allSettings <- (join $ YAML.decodeFile ("config/postgresql.yml" :: String)) >>= fromMapping - settings <- lookupMapping (show env) allSettings - database <- lookupScalar "database" settings :: IO Text + safeRead :: String -> Maybe Int + safeRead = undefined - connPart <- fmap T.concat $ (flip mapM) ["user", "password", "host", "port"] $ \key -> do - value <- lookupScalar key settings - return $ [st| #{key}=#{value} |] - return $ [st|#{connPart} dbname=#{database}|] + addPort :: Bool -> Int -> String + addPort True 443 = "" + addPort False 80 = "" + addPort _ p = ":" ++ show p --- | Load Sqlite settings from a YAML-formatted file located at --- @config\/sqlite.yml@. -loadSqliteConnStr :: Show e => e -> IO Text -loadSqliteConnStr env = do - allSettings <- (join $ YAML.decodeFile ("config/sqlite.yml" :: String)) >>= fromMapping - settings <- lookupMapping (show env) allSettings - lookupScalar "database" settings +loadPostgresqlConnStr :: Show e => e -> IO DbConfig +loadPostgresqlConnStr env = withYamlEnvironment "config/postgresql.yml" env $ \e -> do + db <- lookupScalar "database" e + pool <- lookupScalar "poolsize" e --- note: no type signature to avoid an extra Persistent.MongoDB dep for --- those that don't need it ---loadMongoConnParams :: AppEnvironment -> IO (Database, HostName) -loadMongoConnParams env = do - allSettings <- (join $ YAML.decodeFile ("config/mongoDB.yml" :: String)) >>= fromMapping - settings <- lookupMapping (show env) allSettings - database <- lookupScalar "database" settings - host <- lookupScalar "host" settings - return (database, host) + -- TODO: the rest of this + return $ PostgresConf "todo" db (read pool) + + {-connPart <- fmap T.concat $ (flip mapM) ["user", "password", "host", "port"] $ \key -> do-} + {-value <- lookupScalar key settings-} + {-return $ [st| #{key}=#{value} |]-} + {-return $ [st|#{connPart} dbname=#{database}|]-} + +loadSqliteConnStr :: Show e => e -> IO DbConfig +loadSqliteConnStr env = withYamlEnvironment "config/sqlite.yml" env $ \e -> do + db <- lookupScalar "database" e + pool <- lookupScalar "poolsize" e + + -- TODO: safer read + return $ SqliteConf db (read pool) + +loadMongoConnParams :: Show e => e -> IO DbConfig +loadMongoConnParams env = withYamlEnvironment "config/mongoDB.yml" env $ \e -> do + db <- lookupScalar "database" e + host <- lookupScalar "host" e + pool <- lookupScalar "poolsize" e + + -- TODO: safer read + return $ MongoConf (db, host) (read pool) + +-- TODO: type sig -- ghci and I disagree here... +withYamlEnvironment fp env f = do + obj <- join $ decodeFile fp + case go obj env of + Just v -> return v + Nothing -> error $ fp ++ ": invalid configuration file." + + where + go o e = do + envs <- fromMapping o + conf <- lookupMapping (show e) envs + f conf diff --git a/yesod-default/Yesod/Default/Config.hs b/yesod-default/Yesod/Default/Config.hs index 7f92b3b8..94be769d 100644 --- a/yesod-default/Yesod/Default/Config.hs +++ b/yesod-default/Yesod/Default/Config.hs @@ -21,10 +21,6 @@ data DefaultEnv = Development | Testing | Production deriving (Read, Show, Enum, Bounded) -instance AppEnv DefaultEnv where - displayPort Production = False - displayPort _ = True - -- | Setup commandline arguments for environment and port data ArgConfig = ArgConfig { environment :: String @@ -55,7 +51,7 @@ defaultArgConfig = fromArgs :: IO (AppConfig DefaultEnv) fromArgs = fromArgsWith defaultArgConfig -fromArgsWith :: AppEnv e => ArgConfig -> IO (AppConfig e) +fromArgsWith :: (Read e, Show e) => ArgConfig -> IO (AppConfig e) fromArgsWith argConfig = do args <- cmdArgs argConfig diff --git a/yesod-default/Yesod/Default/Main.hs b/yesod-default/Yesod/Default/Main.hs index 2ac9ee69..f19e5d41 100644 --- a/yesod-default/Yesod/Default/Main.hs +++ b/yesod-default/Yesod/Default/Main.hs @@ -25,7 +25,7 @@ import Network.Wai.Middleware.Debug (debugHandle) -- > main :: IO () -- > main = defaultMain (fromArgsWith customArgConfig) withMySite -- -defaultMain :: AppEnv e => IO (AppConfig e) -> (AppConfig e -> Logger -> (Application -> IO ()) -> IO ()) -> IO () +defaultMain :: (Show e, Read e) => IO (AppConfig e) -> (AppConfig e -> Logger -> (Application -> IO ()) -> IO ()) -> IO () defaultMain load withSite = do config <- load logger <- makeLogger @@ -47,7 +47,7 @@ defaultDevelApp = defaultDevelAppWith loadDevelopmentConfig -- > withDevelAppPort :: Dynamic -- > withDevelAppPort = toDyn $ (defaultDevelAppWith customLoadAppConfig) withMySite -- -defaultDevelAppWith :: AppEnv e +defaultDevelAppWith :: (Show e, Read e) => IO (AppConfig e) -- ^ A means to load your development @'AppConfig'@ -> (AppConfig e -> Logger -> (Application -> IO ()) -> IO ()) -- ^ Your @withMySite@ function -> ((Int, Application) -> IO ()) -> IO () From 8f02508500767be0f3993d55e2a856d2ea9421b4 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Wed, 21 Sep 2011 16:19:46 -0400 Subject: [PATCH 27/44] Finalizing Yesod.Config * Split DbConfig into separate types to ease scaffolding * Add safeRead and type sig on withYaml... * Add documentation --- yesod-core/Yesod/Config.hs | 161 +++++++++++++++++++++++++++---------- 1 file changed, 119 insertions(+), 42 deletions(-) diff --git a/yesod-core/Yesod/Config.hs b/yesod-core/Yesod/Config.hs index 5aa1a9a7..9a10315f 100644 --- a/yesod-core/Yesod/Config.hs +++ b/yesod-core/Yesod/Config.hs @@ -1,21 +1,21 @@ -{-# OPTIONS -fno-warn-missing-signatures #-} -{-# LANGUAGE QuasiQuotes #-} module Yesod.Config ( AppConfig(..) + , PostgresConf(..) + , SqliteConf(..) + , MongoConf(..) , loadConfig - , loadPostgresqlConnStr - , loadSqliteConnStr - , loadMongoConnParams + , loadPostgresql + , loadSqlite + , loadMongo ) where -import Control.Monad (join) +import Control.Monad (join, forM) import Data.Maybe (fromMaybe) import Data.Object import Data.Object.Yaml -import Data.Text (Text) ---import Text.Shakespeare.Text (st) -import qualified Data.Text as T +import Data.Text (Text) +import qualified Data.Text as T -- | Dynamic per-environment configuration which can be loaded at -- run-time negating the need to recompile between environments. @@ -25,15 +25,58 @@ data AppConfig e = AppConfig , appRoot :: Text } deriving (Show) --- | Dynamic per-environment database configuration which can be loaded --- at run-time -data DbConfig = PostgresConf String String Int -- ^ Connection string, Database, Pool size - | SqliteConf String Int -- ^ Database, Pool size - | MongoConf (String,String) Int -- ^ (Database,Host), Pool size +-- separate types means more code here, but it's easier to use in the +-- scaffold --- | Load an @'AppConfig'@ from a YAML-formatted file located at --- @config\/settings.yml@. @'show'@ will be called on the first --- parameter to determine which environment block to load +-- | Information required to connect to a postgres database +data PostgresConf = PostgresConf + { pgConnStr :: String + , pgDatabase :: String + , pgPoolSize :: Int + } + +-- | Information required to connect to a sqlite database +data SqliteConf = SqliteConf + { sqlDatabase :: String + , 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@. +-- +-- Some examples: +-- +-- > -- typical local development +-- > Development: +-- > host: localhost +-- > port: 3000 +-- > +-- > -- ssl: will default false +-- > -- approot: will default to "http://localhost:3000" +-- +-- > -- typical outward-facing production box +-- > Production: +-- > host: www.example.com +-- > +-- > -- ssl: will default false +-- > -- port: will default 80 +-- > -- approot: will default "http://www.example.com" +-- +-- > -- maybe you're reverse proxying connections to the running app +-- > -- on some other port +-- > Production: +-- > port: 8080 +-- > approot: "http://example.com" +-- > +-- > -- approot is specified so that the non-80 port is not appended +-- > -- automatically. +-- loadConfig :: Show e => e -> IO (AppConfig e) loadConfig env = withYamlEnvironment "config/settings.yml" env $ \e -> do let mssl = lookupScalar "ssl" e @@ -42,12 +85,12 @@ loadConfig env = withYamlEnvironment "config/settings.yml" env $ \e -> do let mapproot = lookupScalar "approot" e -- set some default arguments - let ssl = toBool $ fromMaybe "false" mssl + let ssl = maybe False toBool mssl port <- safeRead $ fromMaybe (if ssl then "443" else "80") mport approot <- case (mhost, mapproot) of (_ , Just ar) -> Just ar - (Just host, _ ) -> Just $ (if ssl then "http://" else "https://") ++ host ++ (addPort ssl port) + (Just host, _ ) -> Just $ (if ssl then "https://" else "http://") ++ host ++ (addPort ssl port) _ -> Nothing return $ AppConfig @@ -60,45 +103,73 @@ loadConfig env = withYamlEnvironment "config/settings.yml" env $ \e -> do toBool :: String -> Bool toBool = (`elem` ["true", "TRUE", "yes", "YES", "Y", "1"]) - safeRead :: String -> Maybe Int - safeRead = undefined - addPort :: Bool -> Int -> String addPort True 443 = "" addPort False 80 = "" addPort _ p = ":" ++ show p -loadPostgresqlConnStr :: Show e => e -> IO DbConfig -loadPostgresqlConnStr env = withYamlEnvironment "config/postgresql.yml" env $ \e -> do +-- | 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 <- lookupScalar "poolsize" e + pool <- safeRead =<< lookupScalar "poolsize" e - -- TODO: the rest of this - return $ PostgresConf "todo" db (read pool) + -- TODO: default host/port? + connparts <- forM ["user", "password", "host", "port"] $ \k -> do + v <- lookupScalar k e + return $ k ++ "=" ++ v - {-connPart <- fmap T.concat $ (flip mapM) ["user", "password", "host", "port"] $ \key -> do-} - {-value <- lookupScalar key settings-} - {-return $ [st| #{key}=#{value} |]-} - {-return $ [st|#{connPart} dbname=#{database}|]-} + conn <- return $ concat connparts -loadSqliteConnStr :: Show e => e -> IO DbConfig -loadSqliteConnStr env = withYamlEnvironment "config/sqlite.yml" env $ \e -> do + return $ PostgresConf conn 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 <- lookupScalar "poolsize" e + pool <- safeRead =<< lookupScalar "poolsize" e - -- TODO: safer read - return $ SqliteConf db (read pool) + return $ SqliteConf db pool -loadMongoConnParams :: Show e => e -> IO DbConfig -loadMongoConnParams env = withYamlEnvironment "config/mongoDB.yml" env $ \e -> do +-- | 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 <- lookupScalar "poolsize" e + pool <- safeRead =<< lookupScalar "poolsize" e - -- TODO: safer read - return $ MongoConf (db, host) (read pool) + return $ MongoConf db host pool --- TODO: type sig -- ghci and I disagree here... +-- | Loads the configuration block in the passed file named by the +-- passed environment, yeilds to the passed function as a mapping. +-- +-- Errors in the case of a bad load or if your function returns +-- @Nothing@. +withYamlEnvironment :: (IsYamlScalar v, Show e) + => FilePath -- ^ the yaml file + -> e -- ^ the environment you want to load + -> ([(String, Object String v)] -> Maybe a) -- ^ what to do with the mapping + -> IO a withYamlEnvironment fp env f = do obj <- join $ decodeFile fp case go obj env of @@ -110,3 +181,9 @@ withYamlEnvironment fp env f = do envs <- fromMapping o conf <- lookupMapping (show e) envs f conf + +-- | Returns Nothing if read fails +safeRead :: String -> Maybe Int +safeRead s = case reads s of + (i, _):_ -> Just i + [] -> Nothing From 372f6268d6926eeffb81c40f1f68c195ccbb91d9 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Wed, 21 Sep 2011 16:29:14 -0400 Subject: [PATCH 28/44] updated scripts repo --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index f56426fa..e791ced0 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit f56426fada59012329f23c928a2d7f9c3a515d75 +Subproject commit e791ced0395245e30d37b5098a27bba5e818ecb7 From cfb11cca891dc60b598b51126840bb45098dcc56 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Wed, 21 Sep 2011 17:29:55 -0400 Subject: [PATCH 29/44] Remove -text dep, make Postgresql work... --- yesod-core/Yesod/Config.hs | 7 +++---- yesod-core/yesod-core.cabal | 1 - 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/yesod-core/Yesod/Config.hs b/yesod-core/Yesod/Config.hs index 9a10315f..dcd2902e 100644 --- a/yesod-core/Yesod/Config.hs +++ b/yesod-core/Yesod/Config.hs @@ -30,8 +30,7 @@ data AppConfig e = AppConfig -- | Information required to connect to a postgres database data PostgresConf = PostgresConf - { pgConnStr :: String - , pgDatabase :: String + { pgConnStr :: Text , pgPoolSize :: Int } @@ -126,11 +125,11 @@ loadPostgresql env = withYamlEnvironment "config/postgresql.yml" env $ \e -> do -- TODO: default host/port? connparts <- forM ["user", "password", "host", "port"] $ \k -> do v <- lookupScalar k e - return $ k ++ "=" ++ v + return $ k ++ "=" ++ v ++ " " conn <- return $ concat connparts - return $ PostgresConf conn db pool + return $ PostgresConf (T.pack $ conn ++ " dbname=" ++ db) pool -- | Load a @'SqliteConf'@ from @config\/sqlite.yml@. -- diff --git a/yesod-core/yesod-core.cabal b/yesod-core/yesod-core.cabal index 1007919a..12d2e97b 100644 --- a/yesod-core/yesod-core.cabal +++ b/yesod-core/yesod-core.cabal @@ -38,7 +38,6 @@ library , shakespeare >= 0.10 && < 0.11 , shakespeare-js >= 0.10 && < 0.11 , shakespeare-css >= 0.10 && < 0.11 - , shakespeare-text >= 0.10 && < 0.11 , blaze-builder >= 0.2.1 && < 0.4 , transformers >= 0.2 && < 0.3 , clientsession >= 0.7.2 && < 0.8 From b13875b8a67a6dd2dcc4151ff3078508ef1030e6 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Wed, 21 Sep 2011 18:52:08 -0400 Subject: [PATCH 30/44] make sqlite db a Text --- yesod-core/Yesod/Config.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yesod-core/Yesod/Config.hs b/yesod-core/Yesod/Config.hs index dcd2902e..86ba3873 100644 --- a/yesod-core/Yesod/Config.hs +++ b/yesod-core/Yesod/Config.hs @@ -36,7 +36,7 @@ data PostgresConf = PostgresConf -- | Information required to connect to a sqlite database data SqliteConf = SqliteConf - { sqlDatabase :: String + { sqlDatabase :: Text , sqlPoolSize :: Int } @@ -142,7 +142,7 @@ loadSqlite env = withYamlEnvironment "config/sqlite.yml" env $ \e -> do db <- lookupScalar "database" e pool <- safeRead =<< lookupScalar "poolsize" e - return $ SqliteConf db pool + return $ SqliteConf (T.pack db) pool -- | Load a @'MongoConf'@ from @config\/mongoDB.yml@. -- From a20c28dfad409d8a5631e5b084d37cb5ad91aeb6 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Wed, 21 Sep 2011 18:52:27 -0400 Subject: [PATCH 31/44] add Staging to match scaffolded ymls --- yesod-default/Yesod/Default/Config.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/yesod-default/Yesod/Default/Config.hs b/yesod-default/Yesod/Default/Config.hs index 94be769d..6137fe67 100644 --- a/yesod-default/Yesod/Default/Config.hs +++ b/yesod-default/Yesod/Default/Config.hs @@ -19,6 +19,7 @@ import System.Console.CmdArgs hiding (args) -- Production environments data DefaultEnv = Development | Testing + | Staging | Production deriving (Read, Show, Enum, Bounded) -- | Setup commandline arguments for environment and port From a6e7924e7c7fabce6db447243abcef72065c7ec5 Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Wed, 21 Sep 2011 18:52:57 -0400 Subject: [PATCH 32/44] New Scaffold! hurrah! --- yesod/scaffold/Application.hs.cg | 4 +++- yesod/scaffold/Foundation.hs.cg | 3 ++- yesod/scaffold/Settings.hs.cg | 4 ++-- yesod/scaffold/config/mongoDB.yml.cg | 3 +++ yesod/scaffold/config/postgresql.yml.cg | 4 ++++ yesod/scaffold/config/settings.yml.cg | 1 - yesod/scaffold/config/sqlite.yml.cg | 3 +++ yesod/scaffold/main.hs.cg | 5 +++-- yesod/scaffold/mongoDBConnPool.cg | 6 +++--- yesod/scaffold/postgresqlConnPool.cg | 6 +++--- yesod/scaffold/project.cabal.cg | 1 + yesod/scaffold/sqliteConnPool.cg | 6 +++--- yesod/scaffold/tiny/Application.hs.cg | 6 +++--- yesod/scaffold/tiny/Foundation.hs.cg | 4 ++-- yesod/scaffold/tiny/Settings.hs.cg | 4 ++-- yesod/scaffold/tiny/project.cabal.cg | 2 +- 16 files changed, 38 insertions(+), 24 deletions(-) diff --git a/yesod/scaffold/Application.hs.cg b/yesod/scaffold/Application.hs.cg index 02d95819..6c9a5c65 100644 --- a/yesod/scaffold/Application.hs.cg +++ b/yesod/scaffold/Application.hs.cg @@ -12,6 +12,8 @@ import Foundation import Settings import Settings.StaticFiles (static) import Yesod.Auth +import Yesod.Default.Config +import Yesod.Default.Main import Yesod.Logger (Logger) import Database.Persist.~importGenericDB~ import Data.ByteString (ByteString) @@ -43,7 +45,7 @@ getRobotsR = return $ RepPlain $ toContent ("User-agent: *" :: ByteString) -- performs initialization and creates a WAI application. This is also the -- place to put your migrate statements to have automatic database -- migrations handled by Yesod. -with~sitearg~ :: AppConfig -> Logger -> (Application -> IO a) -> IO () +with~sitearg~ :: AppConfig DefaultEnv -> Logger -> (Application -> IO a) -> IO () with~sitearg~ conf logger f = do s <- static Settings.staticDir Settings.withConnectionPool conf $ \p -> do~runMigration~ diff --git a/yesod/scaffold/Foundation.hs.cg b/yesod/scaffold/Foundation.hs.cg index a925401f..6910cd26 100644 --- a/yesod/scaffold/Foundation.hs.cg +++ b/yesod/scaffold/Foundation.hs.cg @@ -22,6 +22,7 @@ import Settings.StaticFiles import Yesod.Auth import Yesod.Auth.OpenId import Yesod.Auth.Email +import Yesod.Default.Config import Yesod.Logger (Logger, logLazyText) import qualified Settings import System.Directory @@ -45,7 +46,7 @@ import Text.Shakespeare.Text (stext) -- starts running, such as database connections. Every handler will have -- access to the data present here. data ~sitearg~ = ~sitearg~ - { settings :: AppConfig + { settings :: AppConfig DefaultEnv , getLogger :: Logger , getStatic :: Static -- ^ Settings for static file serving. , connPool :: Settings.ConnectionPool -- ^ Database connection pool. diff --git a/yesod/scaffold/Settings.hs.cg b/yesod/scaffold/Settings.hs.cg index 36bd1576..c60fb3f6 100644 --- a/yesod/scaffold/Settings.hs.cg +++ b/yesod/scaffold/Settings.hs.cg @@ -29,7 +29,7 @@ import Text.Shakespeare.Text (st) import Language.Haskell.TH.Syntax import Database.Persist.~importPersist~ import Yesod (liftIO, MonadControlIO, addWidget, addCassius, addJulius, addLucius, whamletFile) -import Yesod.Settings +import Yesod.Default.Config import Data.Monoid (mempty) import System.Directory (doesFileExist) import Data.Text (Text) @@ -55,7 +55,7 @@ staticDir = "static" -- have to make a corresponding change here. -- -- To see how this value is used, see urlRenderOverride in Foundation.hs -staticRoot :: AppConfig -> Text +staticRoot :: AppConfig DefaultEnv -> Text staticRoot conf = [st|#{appRoot conf}/static|] diff --git a/yesod/scaffold/config/mongoDB.yml.cg b/yesod/scaffold/config/mongoDB.yml.cg index 60f74187..b97d7dfa 100644 --- a/yesod/scaffold/config/mongoDB.yml.cg +++ b/yesod/scaffold/config/mongoDB.yml.cg @@ -4,6 +4,7 @@ Default: &defaults host: localhost port: 27017 database: ~project~ + poolsize: 10 Development: <<: *defaults @@ -14,8 +15,10 @@ Test: Staging: database: ~project~_staging + poolsize: 100 <<: *defaults Production: database: ~project~_production + poolsize: 100 <<: *defaults diff --git a/yesod/scaffold/config/postgresql.yml.cg b/yesod/scaffold/config/postgresql.yml.cg index 2f60ddab..aceae393 100644 --- a/yesod/scaffold/config/postgresql.yml.cg +++ b/yesod/scaffold/config/postgresql.yml.cg @@ -4,6 +4,7 @@ Default: &defaults host: localhost port: 5432 database: ~project~ + poolsize: 10 Development: <<: *defaults @@ -14,7 +15,10 @@ Test: Staging: database: ~project~_staging + poolsize: 100 + <<: *defaults Production: database: ~project~_production + poolsize: 100 <<: *defaults diff --git a/yesod/scaffold/config/settings.yml.cg b/yesod/scaffold/config/settings.yml.cg index 1485242c..d6fad856 100644 --- a/yesod/scaffold/config/settings.yml.cg +++ b/yesod/scaffold/config/settings.yml.cg @@ -1,7 +1,6 @@ Default: &defaults host: "http://localhost" port: 3000 - connectionPoolSize: 10 Development: <<: *defaults diff --git a/yesod/scaffold/config/sqlite.yml.cg b/yesod/scaffold/config/sqlite.yml.cg index b9f01df1..ebee1fa8 100644 --- a/yesod/scaffold/config/sqlite.yml.cg +++ b/yesod/scaffold/config/sqlite.yml.cg @@ -1,5 +1,6 @@ Default: &defaults database: ~project~.sqlite3 + poolsize: 10 Development: <<: *defaults @@ -10,8 +11,10 @@ Test: Staging: database: ~project~_staging.sqlite3 + poolsize: 100 <<: *defaults Production: database: ~project~_production.sqlite3 + poolsize: 100 <<: *defaults diff --git a/yesod/scaffold/main.hs.cg b/yesod/scaffold/main.hs.cg index 94f61c28..db9ccf5b 100644 --- a/yesod/scaffold/main.hs.cg +++ b/yesod/scaffold/main.hs.cg @@ -1,5 +1,6 @@ -import Yesod.Main (defaultMain, fromArgs) -import Application (with~sitearg~) +import Yesod.Default.Config (fromArgs) +import Yesod.Default.Main (defaultMain) +import Application (with~sitearg~) main :: IO () main = defaultMain fromArgs with~sitearg~ diff --git a/yesod/scaffold/mongoDBConnPool.cg b/yesod/scaffold/mongoDBConnPool.cg index 4292d22f..4269f2fa 100644 --- a/yesod/scaffold/mongoDBConnPool.cg +++ b/yesod/scaffold/mongoDBConnPool.cg @@ -1,8 +1,8 @@ runConnectionPool :: MonadControlIO m => Action m a -> ConnectionPool -> m a runConnectionPool = runMongoDBConn (ConfirmWrites [u"j" =: True]) -withConnectionPool :: (MonadControlIO m, Applicative m) => AppConfig -> (ConnectionPool -> m b) -> m b +withConnectionPool :: (MonadControlIO m, Applicative m) => AppConfig DefaultEnv -> (ConnectionPool -> m b) -> m b withConnectionPool conf f = do - (database,host) <- liftIO $ loadMongoConnParams (appEnv conf) - withMongoDBPool (u database) host (connectionPoolSize conf) f + dbConf <- liftIO $ loadMongo (appEnv conf) + withMongoDBPool (u $ mgDatabase dbConf) (mgHost dbConf) (mgPoolSize dbConf) f diff --git a/yesod/scaffold/postgresqlConnPool.cg b/yesod/scaffold/postgresqlConnPool.cg index e8597e4f..6619a88e 100644 --- a/yesod/scaffold/postgresqlConnPool.cg +++ b/yesod/scaffold/postgresqlConnPool.cg @@ -1,10 +1,10 @@ runConnectionPool :: MonadControlIO m => SqlPersist m a -> ConnectionPool -> m a runConnectionPool = runSqlPool -withConnectionPool :: MonadControlIO m => AppConfig -> (ConnectionPool -> m a) -> m a +withConnectionPool :: MonadControlIO m => AppConfig DefaultEnv -> (ConnectionPool -> m a) -> m a withConnectionPool conf f = do - cs <- liftIO $ load~upper~ConnStr (appEnv conf) - with~upper~Pool cs (connectionPoolSize conf) f + dbConf <- liftIO $ load~upper~ (appEnv conf) + with~upper~Pool (pgConnStr dbConf) (pgPoolSize dbConf) f -- Example of making a dynamic configuration static -- use /return $(mkConnStr Production)/ instead of loadConnStr diff --git a/yesod/scaffold/project.cabal.cg b/yesod/scaffold/project.cabal.cg index 3109b5ed..28a7f889 100644 --- a/yesod/scaffold/project.cabal.cg +++ b/yesod/scaffold/project.cabal.cg @@ -58,6 +58,7 @@ executable ~project~ , yesod-core , yesod-auth , yesod-static + , yesod-default , blaze-html , yesod-form , mime-mail diff --git a/yesod/scaffold/sqliteConnPool.cg b/yesod/scaffold/sqliteConnPool.cg index e8597e4f..56e1ae9d 100644 --- a/yesod/scaffold/sqliteConnPool.cg +++ b/yesod/scaffold/sqliteConnPool.cg @@ -1,10 +1,10 @@ runConnectionPool :: MonadControlIO m => SqlPersist m a -> ConnectionPool -> m a runConnectionPool = runSqlPool -withConnectionPool :: MonadControlIO m => AppConfig -> (ConnectionPool -> m a) -> m a +withConnectionPool :: MonadControlIO m => AppConfig DefaultEnv -> (ConnectionPool -> m a) -> m a withConnectionPool conf f = do - cs <- liftIO $ load~upper~ConnStr (appEnv conf) - with~upper~Pool cs (connectionPoolSize conf) f + dbConf <- liftIO $ load~upper~ (appEnv conf) + with~upper~Pool (sqlDatabase dbConf) (sqlPoolSize dbConf) f -- Example of making a dynamic configuration static -- use /return $(mkConnStr Production)/ instead of loadConnStr diff --git a/yesod/scaffold/tiny/Application.hs.cg b/yesod/scaffold/tiny/Application.hs.cg index 77c73898..0bd1d218 100644 --- a/yesod/scaffold/tiny/Application.hs.cg +++ b/yesod/scaffold/tiny/Application.hs.cg @@ -11,8 +11,8 @@ module Application import Foundation import Settings import Yesod.Static -import Yesod.Settings -import Yesod.Main (defaultDevelApp) +import Yesod.Default.Config +import Yesod.Default.Main (defaultDevelApp) import Yesod.Logger (Logger) import Data.ByteString (ByteString) import Network.Wai (Application) @@ -38,7 +38,7 @@ getRobotsR = return $ RepPlain $ toContent ("User-agent: *" :: ByteString) -- performs initialization and creates a WAI application. This is also the -- place to put your migrate statements to have automatic database -- migrations handled by Yesod. -with~sitearg~ :: AppConfig -> Logger -> (Application -> IO a) -> IO a +with~sitearg~ :: AppConfig DefaultEnv -> Logger -> (Application -> IO a) -> IO a with~sitearg~ conf logger f = do #ifdef PRODUCTION s <- static Settings.staticDir diff --git a/yesod/scaffold/tiny/Foundation.hs.cg b/yesod/scaffold/tiny/Foundation.hs.cg index 5e950805..58bb57c9 100644 --- a/yesod/scaffold/tiny/Foundation.hs.cg +++ b/yesod/scaffold/tiny/Foundation.hs.cg @@ -14,7 +14,7 @@ module Foundation ) where import Yesod.Core -import Yesod.Settings (AppConfig(..)) +import Yesod.Default.Config import Yesod.Static (Static, base64md5, StaticRoute(..)) import Settings.StaticFiles import Yesod.Logger (Logger, logLazyText) @@ -33,7 +33,7 @@ import Web.ClientSession (getKey) -- starts running, such as database connections. Every handler will have -- access to the data present here. data ~sitearg~ = ~sitearg~ - { settings :: AppConfig + { settings :: AppConfig DefaultEnv , getLogger :: Logger , getStatic :: Static -- ^ Settings for static file serving. } diff --git a/yesod/scaffold/tiny/Settings.hs.cg b/yesod/scaffold/tiny/Settings.hs.cg index a36c764b..605bc709 100644 --- a/yesod/scaffold/tiny/Settings.hs.cg +++ b/yesod/scaffold/tiny/Settings.hs.cg @@ -24,7 +24,7 @@ import qualified Text.Shakespeare.Text as S import Text.Shakespeare.Text (st) import Language.Haskell.TH.Syntax import Yesod.Widget (addWidget, addCassius, addJulius, addLucius, whamletFile) -import Yesod.Settings +import Yesod.Default.Config import Data.Monoid (mempty) import System.Directory (doesFileExist) import Data.Text (Text) @@ -47,7 +47,7 @@ staticDir = "static" -- have to make a corresponding change here. -- -- To see how this value is used, see urlRenderOverride in ~project~.hs -staticRoot :: AppConfig -> Text +staticRoot :: AppConfig DefaultEnv -> Text staticRoot conf = [st|#{appRoot conf}/static|] -- The rest of this file contains settings which rarely need changing by a diff --git a/yesod/scaffold/tiny/project.cabal.cg b/yesod/scaffold/tiny/project.cabal.cg index 6e5c3cc5..1cc7fb75 100644 --- a/yesod/scaffold/tiny/project.cabal.cg +++ b/yesod/scaffold/tiny/project.cabal.cg @@ -46,9 +46,9 @@ executable ~project~ hs-source-dirs: . build-depends: base >= 4 && < 5 - , yesod >= 0.9 && < 0.10 , yesod-core >= 0.9 && < 0.10 , yesod-static + , yesod-default , clientsession , wai-extra , directory From 1dbbfc8d06a1c8099893adb9fa3c7b336d14794b Mon Sep 17 00:00:00 2001 From: Michael Snoyman Date: Thu, 22 Sep 2011 07:32:50 +0300 Subject: [PATCH 33/44] More explicit config error messages; fix defaultArgConfig --- yesod-core/Yesod/Config.hs | 38 +++++++++++---------------- yesod-default/Yesod/Default/Config.hs | 8 +++--- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/yesod-core/Yesod/Config.hs b/yesod-core/Yesod/Config.hs index 86ba3873..51216e59 100644 --- a/yesod-core/Yesod/Config.hs +++ b/yesod-core/Yesod/Config.hs @@ -85,12 +85,12 @@ loadConfig env = withYamlEnvironment "config/settings.yml" env $ \e -> do -- set some default arguments let ssl = maybe False toBool mssl - port <- safeRead $ fromMaybe (if ssl then "443" else "80") mport + port <- safeRead "port" $ fromMaybe (if ssl then "443" else "80") mport approot <- case (mhost, mapproot) of - (_ , Just ar) -> Just ar - (Just host, _ ) -> Just $ (if ssl then "https://" else "http://") ++ host ++ (addPort ssl port) - _ -> Nothing + (_ , Just ar) -> return ar + (Just host, _ ) -> return $ (if ssl then "https://" else "http://") ++ host ++ (addPort ssl port) + _ -> fail "You must supply either a host or approot" return $ AppConfig { appEnv = env @@ -120,7 +120,7 @@ loadConfig env = withYamlEnvironment "config/settings.yml" env $ \e -> do loadPostgresql :: Show e => e -> IO PostgresConf loadPostgresql env = withYamlEnvironment "config/postgresql.yml" env $ \e -> do db <- lookupScalar "database" e - pool <- safeRead =<< lookupScalar "poolsize" e + pool <- safeRead "poolsize" =<< lookupScalar "poolsize" e -- TODO: default host/port? connparts <- forM ["user", "password", "host", "port"] $ \k -> do @@ -140,7 +140,7 @@ loadPostgresql env = withYamlEnvironment "config/postgresql.yml" env $ \e -> do loadSqlite :: Show e => e -> IO SqliteConf loadSqlite env = withYamlEnvironment "config/sqlite.yml" env $ \e -> do db <- lookupScalar "database" e - pool <- safeRead =<< lookupScalar "poolsize" e + pool <- safeRead "poolsize" =<< lookupScalar "poolsize" e return $ SqliteConf (T.pack db) pool @@ -155,7 +155,7 @@ 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 =<< lookupScalar "poolsize" e + pool <- safeRead "poolsize" =<< lookupScalar "poolsize" e return $ MongoConf db host pool @@ -167,22 +167,16 @@ loadMongo env = withYamlEnvironment "config/mongoDB.yml" env $ \e -> do withYamlEnvironment :: (IsYamlScalar v, Show e) => FilePath -- ^ the yaml file -> e -- ^ the environment you want to load - -> ([(String, Object String v)] -> Maybe a) -- ^ what to do with the mapping + -> ([(String, Object String v)] -> IO a) -- ^ what to do with the mapping -> IO a withYamlEnvironment fp env f = do obj <- join $ decodeFile fp - case go obj env of - Just v -> return v - Nothing -> error $ fp ++ ": invalid configuration file." + envs <- fromMapping obj + conf <- lookupMapping (show env) envs + f conf - where - go o e = do - envs <- fromMapping o - conf <- lookupMapping (show e) envs - f conf - --- | Returns Nothing if read fails -safeRead :: String -> Maybe Int -safeRead s = case reads s of - (i, _):_ -> Just i - [] -> Nothing +-- | Returns 'fail' if read fails +safeRead :: Monad m => String -> String -> m Int +safeRead name s = case reads s of + (i, _):_ -> return i + [] -> fail $ concat ["Invalid value for ", name, ": ", s] diff --git a/yesod-default/Yesod/Default/Config.hs b/yesod-default/Yesod/Default/Config.hs index 6137fe67..742967bb 100644 --- a/yesod-default/Yesod/Default/Config.hs +++ b/yesod-default/Yesod/Default/Config.hs @@ -32,8 +32,7 @@ data ArgConfig = ArgConfig defaultArgConfig :: ArgConfig defaultArgConfig = ArgConfig - { environment = def - &= opt "development" + { environment = "development" &= help ("application environment, one of: " ++ environments) &= typ "ENVIRONMENT" , port = def @@ -56,7 +55,10 @@ fromArgsWith :: (Read e, Show e) => ArgConfig -> IO (AppConfig e) fromArgsWith argConfig = do args <- cmdArgs argConfig - let env = read $ capitalize $ environment args + env <- + case reads $ capitalize $ environment args of + (e, _):_ -> return e + [] -> error $ "Invalid environment: " ++ environment args config <- loadConfig env From 632bb4c7ed5cfce844d7b7de6c1f0099b27bfe6f Mon Sep 17 00:00:00 2001 From: Michael Snoyman Date: Thu, 22 Sep 2011 08:44:57 +0300 Subject: [PATCH 34/44] addStaticContentExternal --- yesod-default/Yesod/Default/Util.hs | 43 +++++++++++++++++++++++++++++ yesod-default/yesod-default.cabal | 17 ++++++++---- yesod/scaffold/Foundation.hs.cg | 21 ++------------ 3 files changed, 57 insertions(+), 24 deletions(-) create mode 100644 yesod-default/Yesod/Default/Util.hs diff --git a/yesod-default/Yesod/Default/Util.hs b/yesod-default/Yesod/Default/Util.hs new file mode 100644 index 00000000..ba36cb14 --- /dev/null +++ b/yesod-default/Yesod/Default/Util.hs @@ -0,0 +1,43 @@ +{-# LANGUAGE OverloadedStrings #-} +-- | Various utilities used in the scaffolded site. +module Yesod.Default.Util + ( addStaticContentExternal + ) where + +import Control.Monad.IO.Class (liftIO) +import qualified Data.ByteString.Lazy as L +import Data.Text (Text, pack, unpack) +import Yesod.Core -- purposely using complete import so that Haddock will see addStaticContent +import Control.Monad (unless) +import System.Directory (doesFileExist, createDirectoryIfMissing) + +-- | An implementation of 'addStaticContent' which stores the contents in an +-- external file. Files are created in the given static folder with names based +-- on a hash of their content. This allows expiration dates to be set far in +-- the future without worry of users receiving stale content. +addStaticContentExternal + :: (L.ByteString -> Either a L.ByteString) -- ^ javascript minifier + -> (L.ByteString -> String) -- ^ hash function to determine file name + -> FilePath -- ^ location of static directory. files will be placed within a "tmp" subfolder + -> ([Text] -> Route master) -- ^ route constructor, taking a list of pieces + -> Text -- ^ filename extension + -> Text -- ^ mime type + -> L.ByteString -- ^ file contents + -> GHandler sub master (Maybe (Either Text (Route master, [(Text, Text)]))) +addStaticContentExternal minify hash staticDir toRoute ext' _ content = do + liftIO $ createDirectoryIfMissing True statictmp + exists <- liftIO $ doesFileExist fn' + unless exists $ liftIO $ L.writeFile fn' content' + return $ Just $ Right (toRoute ["tmp", pack fn], []) + where + fn, statictmp, fn' :: FilePath + -- by basing the hash off of the un-minified content, we avoid a costly + -- minification if the file already exists + fn = hash content ++ '.' : unpack ext' + statictmp = staticDir ++ "/tmp/" + fn' = statictmp ++ fn + + content' :: L.ByteString + content' + | ext' == "js" = either (const content) id $ minify content + | otherwise = content diff --git a/yesod-default/yesod-default.cabal b/yesod-default/yesod-default.cabal index 306b6fb1..8eea3d63 100644 --- a/yesod-default/yesod-default.cabal +++ b/yesod-default/yesod-default.cabal @@ -14,15 +14,20 @@ description: Convenient wrappers for your the configuration and execution of your yesod application library - build-depends: base >= 4 && < 5 - , yesod-core >= 0.9 && < 0.10 - , cmdargs >= 0.8 && < 0.9 - , warp >= 0.4 && < 0.5 - , wai >= 0.4 && < 0.5 - , wai-extra >= 0.4 && < 0.5 + build-depends: base >= 4 && < 5 + , yesod-core >= 0.9 && < 0.10 + , cmdargs >= 0.8 && < 0.9 + , warp >= 0.4 && < 0.5 + , wai >= 0.4 && < 0.5 + , wai-extra >= 0.4 && < 0.5 + , bytestring >= 0.9 && < 0.10 + , transformers >= 0.2 && < 0.3 + , text >= 0.9 && < 1.0 + , directory >= 1.0 && < 1.2 exposed-modules: Yesod.Default.Config , Yesod.Default.Main + , Yesod.Default.Util ghc-options: -Wall diff --git a/yesod/scaffold/Foundation.hs.cg b/yesod/scaffold/Foundation.hs.cg index 6910cd26..1aa391c1 100644 --- a/yesod/scaffold/Foundation.hs.cg +++ b/yesod/scaffold/Foundation.hs.cg @@ -23,19 +23,18 @@ import Yesod.Auth import Yesod.Auth.OpenId import Yesod.Auth.Email import Yesod.Default.Config +import Yesod.Default.Util (addStaticContentExternal) import Yesod.Logger (Logger, logLazyText) import qualified Settings -import System.Directory import qualified Data.ByteString.Lazy as L import Database.Persist.~importGenericDB~ import Settings (hamletFile, cassiusFile, luciusFile, juliusFile, widgetFile) import Model import Data.Maybe (isJust) -import Control.Monad (join, unless) +import Control.Monad (join) import Network.Mail.Mime import qualified Data.Text.Lazy.Encoding import Text.Jasmine (minifym) -import qualified Data.Text as T import Web.ClientSession (getKey) import Text.Blaze.Renderer.Utf8 (renderHtml) import Text.Hamlet (shamlet) @@ -104,21 +103,7 @@ instance Yesod ~sitearg~ where -- and names them based on a hash of their content. This allows -- expiration dates to be set far in the future without worry of -- users receiving stale content. - addStaticContent ext' _ content = do - let fn = base64md5 content ++ '.' : T.unpack ext' - let content' = - if ext' == "js" - then case minifym content of - Left _ -> content - Right y -> y - else content - let statictmp = Settings.staticDir ++ "/tmp/" - liftIO $ createDirectoryIfMissing True statictmp - let fn' = statictmp ++ fn - exists <- liftIO $ doesFileExist fn' - unless exists $ liftIO $ L.writeFile fn' content' - return $ Just $ Right (StaticR $ StaticRoute ["tmp", T.pack fn] [], []) - + addStaticContent = addStaticContentExternal minifym base64md5 Settings.staticDir (StaticR . flip StaticRoute []) -- How to run database actions. instance YesodPersist ~sitearg~ where From bb7f7c82f2ad2ee2f7e5262c72d59dacc15cd8b0 Mon Sep 17 00:00:00 2001 From: Michael Snoyman Date: Thu, 22 Sep 2011 08:55:18 +0300 Subject: [PATCH 35/44] normalize is a Lucius file --- yesod/Scaffolding/Scaffolder.hs | 4 ++-- yesod/scaffold/Foundation.hs.cg | 1 + yesod/scaffold/hamlet/boilerplate-layout.hamlet.cg | 1 - yesod/scaffold/hamlet/default-layout.hamlet.cg | 1 - .../css/normalize.css.cg => lucius/normalize.lucius.cg} | 0 yesod/scaffold/tiny/Foundation.hs.cg | 1 + yesod/yesod.cabal | 2 +- 7 files changed, 5 insertions(+), 5 deletions(-) rename yesod/scaffold/{static/css/normalize.css.cg => lucius/normalize.lucius.cg} (100%) diff --git a/yesod/Scaffolding/Scaffolder.hs b/yesod/Scaffolding/Scaffolder.hs index 293032b6..9bf2cd0a 100644 --- a/yesod/Scaffolding/Scaffolder.hs +++ b/yesod/Scaffolding/Scaffolder.hs @@ -149,8 +149,8 @@ scaffold = do $(codegen "hamlet/default-layout.hamlet") writeFile' "hamlet/boilerplate-layout.hamlet" $(codegen "hamlet/boilerplate-layout.hamlet") - writeFile' "static/css/normalize.css" - $(codegen "static/css/normalize.css") + writeFile' "lucius/normalize.lucius" + $(codegen "lucius/normalize.lucius") writeFile' "hamlet/homepage.hamlet" $ ifTiny $(codegen "tiny/hamlet/homepage.hamlet") $(codegen "hamlet/homepage.hamlet") writeFile' "config/routes" $ ifTiny $(codegen "tiny/config/routes") $(codegen "config/routes") writeFile' "cassius/homepage.cassius" $(codegen "cassius/homepage.cassius") diff --git a/yesod/scaffold/Foundation.hs.cg b/yesod/scaffold/Foundation.hs.cg index 1aa391c1..42d81293 100644 --- a/yesod/scaffold/Foundation.hs.cg +++ b/yesod/scaffold/Foundation.hs.cg @@ -84,6 +84,7 @@ instance Yesod ~sitearg~ where mmsg <- getMessage pc <- widgetToPageContent $ do widget + addLucius $(luciusFile "normalize") addCassius $(cassiusFile "default-layout") hamletToRepHtml $(hamletFile "default-layout") diff --git a/yesod/scaffold/hamlet/boilerplate-layout.hamlet.cg b/yesod/scaffold/hamlet/boilerplate-layout.hamlet.cg index 522df932..22a5aca4 100644 --- a/yesod/scaffold/hamlet/boilerplate-layout.hamlet.cg +++ b/yesod/scaffold/hamlet/boilerplate-layout.hamlet.cg @@ -15,7 +15,6 @@ #{pageTitle pc} - <link rel="stylesheet" href=@{StaticR css_normalize_css}> ^{pageHead pc} <!--[if lt IE 9]> diff --git a/yesod/scaffold/hamlet/default-layout.hamlet.cg b/yesod/scaffold/hamlet/default-layout.hamlet.cg index 0a4d08f4..f31acb19 100644 --- a/yesod/scaffold/hamlet/default-layout.hamlet.cg +++ b/yesod/scaffold/hamlet/default-layout.hamlet.cg @@ -2,7 +2,6 @@ <html <head <title>#{pageTitle pc} - <link rel="stylesheet" href=@{StaticR css_normalize_css}> ^{pageHead pc} <body $maybe msg <- mmsg diff --git a/yesod/scaffold/static/css/normalize.css.cg b/yesod/scaffold/lucius/normalize.lucius.cg similarity index 100% rename from yesod/scaffold/static/css/normalize.css.cg rename to yesod/scaffold/lucius/normalize.lucius.cg diff --git a/yesod/scaffold/tiny/Foundation.hs.cg b/yesod/scaffold/tiny/Foundation.hs.cg index 58bb57c9..1f4262dc 100644 --- a/yesod/scaffold/tiny/Foundation.hs.cg +++ b/yesod/scaffold/tiny/Foundation.hs.cg @@ -71,6 +71,7 @@ instance Yesod ~sitearg~ where mmsg <- getMessage pc <- widgetToPageContent $ do widget + addLucius $(luciusFile "normalize") addCassius $(cassiusFile "default-layout") hamletToRepHtml $(hamletFile "default-layout") diff --git a/yesod/yesod.cabal b/yesod/yesod.cabal index 9b13dcdf..7fc47113 100644 --- a/yesod/yesod.cabal +++ b/yesod/yesod.cabal @@ -30,7 +30,7 @@ extra-source-files: scaffold/tiny/Handler/Root.hs.cg scaffold/tiny/config/routes.cg scaffold/tiny/Settings.hs.cg - scaffold/static/css/normalize.css.cg + scaffold/lucius/normalize.lucius.cg scaffold/postgresqlConnPool.cg scaffold/sqliteConnPool.cg scaffold/.ghci.cg From 9815fe8891848d169baf4ae56321df26639d8e86 Mon Sep 17 00:00:00 2001 From: Michael Snoyman <michael@snoyman.com> Date: Thu, 22 Sep 2011 08:59:52 +0300 Subject: [PATCH 36/44] tiny/Foundation uses addStaticContentExternal --- yesod/scaffold/tiny/Foundation.hs.cg | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/yesod/scaffold/tiny/Foundation.hs.cg b/yesod/scaffold/tiny/Foundation.hs.cg index 1f4262dc..38152df9 100644 --- a/yesod/scaffold/tiny/Foundation.hs.cg +++ b/yesod/scaffold/tiny/Foundation.hs.cg @@ -15,17 +15,14 @@ module Foundation import Yesod.Core import Yesod.Default.Config +import Yesod.Default.Util (addStaticContentExternal) import Yesod.Static (Static, base64md5, StaticRoute(..)) import Settings.StaticFiles import Yesod.Logger (Logger, logLazyText) import qualified Settings -import System.Directory -import qualified Data.ByteString.Lazy as L import Settings (hamletFile, cassiusFile, luciusFile, juliusFile, widgetFile) -import Control.Monad (unless) import Control.Monad.Trans.Class (lift) import Control.Monad.IO.Class (liftIO) -import qualified Data.Text as T import Web.ClientSession (getKey) -- | The site argument for your application. This can be a good place to @@ -88,11 +85,4 @@ instance Yesod ~sitearg~ where -- and names them based on a hash of their content. This allows -- expiration dates to be set far in the future without worry of -- users receiving stale content. - addStaticContent ext' _ content = do - let fn = base64md5 content ++ '.' : T.unpack ext' - let statictmp = Settings.staticDir ++ "/tmp/" - liftIO $ createDirectoryIfMissing True statictmp - let fn' = statictmp ++ fn - exists <- liftIO $ doesFileExist fn' - unless exists $ liftIO $ L.writeFile fn' content - return $ Just $ Right (StaticR $ StaticRoute ["tmp", T.pack fn] [], []) + addStaticContent = addStaticContentExternal (const $ Left ()) base64md5 Settings.staticDir (StaticR . flip StaticRoute []) From 29ce11cd5ac6ad230ced9b02b9a4e3236448221c Mon Sep 17 00:00:00 2001 From: Michael Snoyman <michael@snoyman.com> Date: Thu, 22 Sep 2011 09:01:27 +0300 Subject: [PATCH 37/44] Quicker builds on development --- yesod/main.hs | 2 +- yesod/scaffold/project.cabal.cg | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/yesod/main.hs b/yesod/main.hs index 988edfc6..a5719efa 100755 --- a/yesod/main.hs +++ b/yesod/main.hs @@ -36,7 +36,7 @@ main = do #endif ["devel"] -> devel isDev ["version"] -> putStrLn "0.9" - "configure":rest -> rawSystem cmd ("configure":rest) >>= exitWith + "configure":rest -> rawSystem cmd ("configure":"--disable-library-profiling":rest) >>= exitWith _ -> do putStrLn "Usage: yesod <command>" putStrLn "Available commands:" diff --git a/yesod/scaffold/project.cabal.cg b/yesod/scaffold/project.cabal.cg index 28a7f889..3055a7e6 100644 --- a/yesod/scaffold/project.cabal.cg +++ b/yesod/scaffold/project.cabal.cg @@ -37,6 +37,8 @@ library Settings.StaticFiles Handler.Root + ghc-options: -Wall -threaded -O0 + executable ~project~ if flag(devel) Buildable: False @@ -45,7 +47,7 @@ executable ~project~ cpp-options: -DPRODUCTION ghc-options: -Wall -threaded -O2 else - ghc-options: -Wall -threaded + ghc-options: -Wall -threaded -O0 if os(windows) cpp-options: -DWINDOWS From 8c094f154ff53d8e953553fb8931afca0ee5dd55 Mon Sep 17 00:00:00 2001 From: Michael Snoyman <michael@snoyman.com> Date: Thu, 22 Sep 2011 09:02:27 +0300 Subject: [PATCH 38/44] Fixed settings.yml.cg --- yesod/scaffold/config/settings.yml.cg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/yesod/scaffold/config/settings.yml.cg b/yesod/scaffold/config/settings.yml.cg index d6fad856..816a2db6 100644 --- a/yesod/scaffold/config/settings.yml.cg +++ b/yesod/scaffold/config/settings.yml.cg @@ -1,5 +1,5 @@ Default: &defaults - host: "http://localhost" + host: "localhost" port: 3000 Development: @@ -12,4 +12,5 @@ Staging: <<: *defaults Production: + approot: "http://www.example.com" <<: *defaults From 31dd38d1039a1ffa724407e28e64dfc34ce459f1 Mon Sep 17 00:00:00 2001 From: Michael Snoyman <michael@snoyman.com> Date: Thu, 22 Sep 2011 09:07:59 +0300 Subject: [PATCH 39/44] Unified homepage between main and tiny scaffolding --- yesod/Scaffolding/Scaffolder.hs | 4 ++-- yesod/scaffold/Handler/Root.hs.cg | 1 - yesod/scaffold/hamlet/homepage.hamlet.cg | 11 ----------- yesod/scaffold/tiny/Handler/Root.hs.cg | 18 ------------------ yesod/scaffold/tiny/hamlet/homepage.hamlet.cg | 2 -- yesod/yesod.cabal | 2 -- 6 files changed, 2 insertions(+), 36 deletions(-) delete mode 100644 yesod/scaffold/tiny/Handler/Root.hs.cg delete mode 100644 yesod/scaffold/tiny/hamlet/homepage.hamlet.cg diff --git a/yesod/Scaffolding/Scaffolder.hs b/yesod/Scaffolding/Scaffolder.hs index 9bf2cd0a..ddede346 100644 --- a/yesod/Scaffolding/Scaffolder.hs +++ b/yesod/Scaffolding/Scaffolder.hs @@ -139,7 +139,7 @@ scaffold = do writeFile' "LICENSE" $(codegen "LICENSE") writeFile' ("Foundation.hs") $ ifTiny $(codegen "tiny/Foundation.hs") $(codegen "Foundation.hs") writeFile' "Application.hs" $ ifTiny $(codegen "tiny/Application.hs") $(codegen "Application.hs") - writeFile' "Handler/Root.hs" $ ifTiny $(codegen "tiny/Handler/Root.hs") $(codegen "Handler/Root.hs") + writeFile' "Handler/Root.hs" $(codegen "Handler/Root.hs") unless isTiny $ writeFile' "Model.hs" $(codegen "Model.hs") writeFile' "Settings.hs" $ ifTiny $(codegen "tiny/Settings.hs") $(codegen "Settings.hs") writeFile' "Settings/StaticFiles.hs" $(codegen "Settings/StaticFiles.hs") @@ -151,7 +151,7 @@ scaffold = do $(codegen "hamlet/boilerplate-layout.hamlet") writeFile' "lucius/normalize.lucius" $(codegen "lucius/normalize.lucius") - writeFile' "hamlet/homepage.hamlet" $ ifTiny $(codegen "tiny/hamlet/homepage.hamlet") $(codegen "hamlet/homepage.hamlet") + writeFile' "hamlet/homepage.hamlet" $(codegen "hamlet/homepage.hamlet") writeFile' "config/routes" $ ifTiny $(codegen "tiny/config/routes") $(codegen "config/routes") writeFile' "cassius/homepage.cassius" $(codegen "cassius/homepage.cassius") writeFile' "julius/homepage.julius" $(codegen "julius/homepage.julius") diff --git a/yesod/scaffold/Handler/Root.hs.cg b/yesod/scaffold/Handler/Root.hs.cg index 99bf711d..e485b7cd 100644 --- a/yesod/scaffold/Handler/Root.hs.cg +++ b/yesod/scaffold/Handler/Root.hs.cg @@ -12,7 +12,6 @@ import Foundation -- inclined, or create a single monolithic file. getRootR :: Handler RepHtml getRootR = do - mu <- maybeAuth defaultLayout $ do h2id <- lift newIdent setTitle "~project~ homepage" diff --git a/yesod/scaffold/hamlet/homepage.hamlet.cg b/yesod/scaffold/hamlet/homepage.hamlet.cg index 727f0eb6..34432b74 100644 --- a/yesod/scaffold/hamlet/homepage.hamlet.cg +++ b/yesod/scaffold/hamlet/homepage.hamlet.cg @@ -1,13 +1,2 @@ <h1>Hello <h2 ##{h2id}>You do not have Javascript enabled. -$maybe u <- mu - <p - You are logged in as #{userIdent $ snd u}. # - <a href=@{AuthR LogoutR}>Logout - . -$nothing - <p - You are not logged in. # - <a href=@{AuthR LoginR}>Login now - . - diff --git a/yesod/scaffold/tiny/Handler/Root.hs.cg b/yesod/scaffold/tiny/Handler/Root.hs.cg deleted file mode 100644 index e485b7cd..00000000 --- a/yesod/scaffold/tiny/Handler/Root.hs.cg +++ /dev/null @@ -1,18 +0,0 @@ -{-# LANGUAGE TemplateHaskell, QuasiQuotes, OverloadedStrings #-} -module Handler.Root where - -import Foundation - --- This is a handler function for the GET request method on the RootR --- resource pattern. All of your resource patterns are defined in --- config/routes --- --- The majority of the code you will write in Yesod lives in these handler --- functions. You can spread them across multiple files if you are so --- inclined, or create a single monolithic file. -getRootR :: Handler RepHtml -getRootR = do - defaultLayout $ do - h2id <- lift newIdent - setTitle "~project~ homepage" - addWidget $(widgetFile "homepage") diff --git a/yesod/scaffold/tiny/hamlet/homepage.hamlet.cg b/yesod/scaffold/tiny/hamlet/homepage.hamlet.cg deleted file mode 100644 index 34432b74..00000000 --- a/yesod/scaffold/tiny/hamlet/homepage.hamlet.cg +++ /dev/null @@ -1,2 +0,0 @@ -<h1>Hello -<h2 ##{h2id}>You do not have Javascript enabled. diff --git a/yesod/yesod.cabal b/yesod/yesod.cabal index 7fc47113..08543eab 100644 --- a/yesod/yesod.cabal +++ b/yesod/yesod.cabal @@ -26,8 +26,6 @@ extra-source-files: scaffold/tiny/Foundation.hs.cg scaffold/tiny/project.cabal.cg scaffold/tiny/Application.hs.cg - scaffold/tiny/hamlet/homepage.hamlet.cg - scaffold/tiny/Handler/Root.hs.cg scaffold/tiny/config/routes.cg scaffold/tiny/Settings.hs.cg scaffold/lucius/normalize.lucius.cg From eb42fbd976bfcf15ace1c3617058974a325cdd42 Mon Sep 17 00:00:00 2001 From: Michael Snoyman <michael@snoyman.com> Date: Thu, 22 Sep 2011 09:26:36 +0300 Subject: [PATCH 40/44] staticFilesList --- yesod-static/Yesod/Static.hs | 30 +++++++++++++++++++++++++++++- yesod-static/yesod-static.cabal | 2 +- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/yesod-static/Yesod/Static.hs b/yesod-static/Yesod/Static.hs index f26813b7..be5e45e8 100644 --- a/yesod-static/Yesod/Static.hs +++ b/yesod-static/Yesod/Static.hs @@ -29,6 +29,7 @@ module Yesod.Static , embed -- * Template Haskell helpers , staticFiles + , staticFilesList , publicFiles -- * Hashing , base64md5 @@ -155,6 +156,25 @@ getFileListPieces = flip go id staticFiles :: Prelude.FilePath -> Q [Dec] staticFiles dir = mkStaticFiles dir +-- | Same as 'staticFiles', but takes an explicit list of files to create +-- identifiers for. The files are given relative to the static folder. For +-- example, to get the files \"static/js/jquery.js\" and +-- \"static/css/normalize.css\", you would use: +-- +-- > staticFilesList "static" ["js/jquery.js"], ["css/normalize.css"]] +-- +-- This can be useful when you have a very large number of static files, but +-- only need to refer to a few of them from Haskell. +staticFilesList :: Prelude.FilePath -> [Prelude.FilePath] -> Q [Dec] +staticFilesList dir fs = + mkStaticFilesList dir (map split fs) "StaticRoute" True + where + split :: Prelude.FilePath -> [String] + split [] = [] + split x = + let (a, b) = break (== '/') x + in a : split (drop 1 b) + -- | like staticFiles, but doesn't append an etag to the query string -- This will compile faster, but doesn't achieve as great of caching. -- The browser can avoid downloading the file, but it always needs to send a request with the etag value or the last-modified value to the server to see if its copy is up to dat @@ -212,6 +232,15 @@ mkStaticFiles' :: Prelude.FilePath -- ^ static directory -> Q [Dec] mkStaticFiles' fp routeConName makeHash = do fs <- qRunIO $ getFileListPieces fp + mkStaticFilesList fp fs routeConName makeHash + +mkStaticFilesList + :: Prelude.FilePath -- ^ static directory + -> [[String]] -- ^ list of files to create identifiers for + -> String -- ^ route constructor "StaticRoute" + -> Bool -- ^ append checksum query parameter + -> Q [Dec] +mkStaticFilesList fp fs routeConName makeHash = do concat `fmap` mapM mkRoute fs where replace' c @@ -233,7 +262,6 @@ mkStaticFiles' fp routeConName makeHash = do pack' <- [|pack|] qs <- if makeHash then do hash <- qRunIO $ base64md5File $ pathFromRawPieces fp f - -- FIXME hash <- qRunIO . calcHash $ fp ++ '/' : intercalate "/" f [|[(pack $(lift hash), mempty)]|] else return $ ListE [] return diff --git a/yesod-static/yesod-static.cabal b/yesod-static/yesod-static.cabal index e503db0c..18e26a80 100644 --- a/yesod-static/yesod-static.cabal +++ b/yesod-static/yesod-static.cabal @@ -1,5 +1,5 @@ name: yesod-static -version: 0.3.0.1 +version: 0.3.1 license: BSD3 license-file: LICENSE author: Michael Snoyman <michael@snoyman.com> From ecca686b73ad7f59bebdffad92fa468a1c91ad49 Mon Sep 17 00:00:00 2001 From: Michael Snoyman <michael@snoyman.com> Date: Thu, 22 Sep 2011 09:37:22 +0300 Subject: [PATCH 41/44] Cassius -> Lucius --- yesod/Scaffolding/Scaffolder.hs | 6 +++--- yesod/scaffold/Foundation.hs.cg | 4 ++-- yesod/scaffold/cassius/default-layout.cassius.cg | 3 --- yesod/scaffold/lucius/default-layout.lucius.cg | 4 ++++ .../homepage.cassius.cg => lucius/homepage.lucius.cg} | 6 ++++-- yesod/scaffold/tiny/Foundation.hs.cg | 4 ++-- yesod/yesod.cabal | 4 ++-- 7 files changed, 17 insertions(+), 14 deletions(-) delete mode 100644 yesod/scaffold/cassius/default-layout.cassius.cg create mode 100644 yesod/scaffold/lucius/default-layout.lucius.cg rename yesod/scaffold/{cassius/homepage.cassius.cg => lucius/homepage.lucius.cg} (64%) diff --git a/yesod/Scaffolding/Scaffolder.hs b/yesod/Scaffolding/Scaffolder.hs index ddede346..bb3dadc2 100644 --- a/yesod/Scaffolding/Scaffolder.hs +++ b/yesod/Scaffolding/Scaffolder.hs @@ -143,8 +143,8 @@ scaffold = do unless isTiny $ writeFile' "Model.hs" $(codegen "Model.hs") writeFile' "Settings.hs" $ ifTiny $(codegen "tiny/Settings.hs") $(codegen "Settings.hs") writeFile' "Settings/StaticFiles.hs" $(codegen "Settings/StaticFiles.hs") - writeFile' "cassius/default-layout.cassius" - $(codegen "cassius/default-layout.cassius") + writeFile' "lucius/default-layout.lucius" + $(codegen "lucius/default-layout.lucius") writeFile' "hamlet/default-layout.hamlet" $(codegen "hamlet/default-layout.hamlet") writeFile' "hamlet/boilerplate-layout.hamlet" @@ -153,7 +153,7 @@ scaffold = do $(codegen "lucius/normalize.lucius") writeFile' "hamlet/homepage.hamlet" $(codegen "hamlet/homepage.hamlet") writeFile' "config/routes" $ ifTiny $(codegen "tiny/config/routes") $(codegen "config/routes") - writeFile' "cassius/homepage.cassius" $(codegen "cassius/homepage.cassius") + writeFile' "lucius/homepage.lucius" $(codegen "lucius/homepage.lucius") writeFile' "julius/homepage.julius" $(codegen "julius/homepage.julius") unless isTiny $ writeFile' "config/models" $(codegen "config/models") diff --git a/yesod/scaffold/Foundation.hs.cg b/yesod/scaffold/Foundation.hs.cg index 42d81293..11c82fa5 100644 --- a/yesod/scaffold/Foundation.hs.cg +++ b/yesod/scaffold/Foundation.hs.cg @@ -84,8 +84,8 @@ instance Yesod ~sitearg~ where mmsg <- getMessage pc <- widgetToPageContent $ do widget - addLucius $(luciusFile "normalize") - addCassius $(cassiusFile "default-layout") + toWidget $(luciusFile "normalize") + toWidget $(luciusFile "default-layout") hamletToRepHtml $(hamletFile "default-layout") -- This is done to provide an optimization for serving static files from diff --git a/yesod/scaffold/cassius/default-layout.cassius.cg b/yesod/scaffold/cassius/default-layout.cassius.cg deleted file mode 100644 index 77177469..00000000 --- a/yesod/scaffold/cassius/default-layout.cassius.cg +++ /dev/null @@ -1,3 +0,0 @@ -body - font-family: sans-serif - diff --git a/yesod/scaffold/lucius/default-layout.lucius.cg b/yesod/scaffold/lucius/default-layout.lucius.cg new file mode 100644 index 00000000..799dec4f --- /dev/null +++ b/yesod/scaffold/lucius/default-layout.lucius.cg @@ -0,0 +1,4 @@ +body { + font-family: sans-serif; +} + diff --git a/yesod/scaffold/cassius/homepage.cassius.cg b/yesod/scaffold/lucius/homepage.lucius.cg similarity index 64% rename from yesod/scaffold/cassius/homepage.cassius.cg rename to yesod/scaffold/lucius/homepage.lucius.cg index 2ac20924..e8cf5292 100644 --- a/yesod/scaffold/cassius/homepage.cassius.cg +++ b/yesod/scaffold/lucius/homepage.lucius.cg @@ -1,5 +1,7 @@ -h1 +h1 { text-align: center -h2##{h2id} +} +h2##{h2id} { color: #990 +} diff --git a/yesod/scaffold/tiny/Foundation.hs.cg b/yesod/scaffold/tiny/Foundation.hs.cg index 38152df9..d80e957e 100644 --- a/yesod/scaffold/tiny/Foundation.hs.cg +++ b/yesod/scaffold/tiny/Foundation.hs.cg @@ -68,8 +68,8 @@ instance Yesod ~sitearg~ where mmsg <- getMessage pc <- widgetToPageContent $ do widget - addLucius $(luciusFile "normalize") - addCassius $(cassiusFile "default-layout") + toWidget $(luciusFile "normalize") + toWidget $(luciusFile "default-layout") hamletToRepHtml $(hamletFile "default-layout") -- This is done to provide an optimization for serving static files from diff --git a/yesod/yesod.cabal b/yesod/yesod.cabal index 08543eab..4e43b436 100644 --- a/yesod/yesod.cabal +++ b/yesod/yesod.cabal @@ -17,8 +17,8 @@ homepage: http://www.yesodweb.com/ extra-source-files: input/*.cg - scaffold/cassius/default-layout.cassius.cg - scaffold/cassius/homepage.cassius.cg + scaffold/lucius/default-layout.lucius.cg + scaffold/lucius/homepage.lucius.cg scaffold/Model.hs.cg scaffold/Foundation.hs.cg scaffold/LICENSE.cg From 189b3d2bab38a3f4d44a5f004f5e7e9eef281767 Mon Sep 17 00:00:00 2001 From: Michael Snoyman <michael@snoyman.com> Date: Thu, 22 Sep 2011 09:50:12 +0300 Subject: [PATCH 42/44] Included messages in scaffolded site --- yesod/Scaffolding/Scaffolder.hs | 4 +++- yesod/scaffold/Foundation.hs.cg | 4 ++++ yesod/scaffold/hamlet/homepage.hamlet.cg | 2 +- yesod/scaffold/messages/en.msg.cg | 1 + yesod/scaffold/tiny/Foundation.hs.cg | 6 +++++- yesod/yesod.cabal | 1 + 6 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 yesod/scaffold/messages/en.msg.cg diff --git a/yesod/Scaffolding/Scaffolder.hs b/yesod/Scaffolding/Scaffolder.hs index bb3dadc2..5cd5569e 100644 --- a/yesod/Scaffolding/Scaffolder.hs +++ b/yesod/Scaffolding/Scaffolder.hs @@ -120,6 +120,7 @@ scaffold = do mkDir "Model" mkDir "deploy" mkDir "Settings" + mkDir "messages" writeFile' ("deploy/Procfile") $(codegen "deploy/Procfile") @@ -156,7 +157,8 @@ scaffold = do writeFile' "lucius/homepage.lucius" $(codegen "lucius/homepage.lucius") writeFile' "julius/homepage.julius" $(codegen "julius/homepage.julius") unless isTiny $ writeFile' "config/models" $(codegen "config/models") - + writeFile' "messages/en.msg" $(codegen "messages/en.msg") + S.writeFile (dir ++ "/config/favicon.ico") $(runIO (S.readFile "scaffold/config/favicon.ico.cg") >>= \bs -> do pack <- [|S.pack|] diff --git a/yesod/scaffold/Foundation.hs.cg b/yesod/scaffold/Foundation.hs.cg index 11c82fa5..d0740589 100644 --- a/yesod/scaffold/Foundation.hs.cg +++ b/yesod/scaffold/Foundation.hs.cg @@ -4,6 +4,7 @@ module Foundation ( ~sitearg~ (..) , ~sitearg~Route (..) + , ~sitearg~Message (..) , resources~sitearg~ , Handler , Widget @@ -51,6 +52,9 @@ data ~sitearg~ = ~sitearg~ , connPool :: Settings.ConnectionPool -- ^ Database connection pool. } +-- Set up i18n messages. See the message folder. +mkMessage "~sitearg~" "messages" "en" + -- This is where we define all of the routes in our application. For a full -- explanation of the syntax, please see: -- http://www.yesodweb.com/book/handler diff --git a/yesod/scaffold/hamlet/homepage.hamlet.cg b/yesod/scaffold/hamlet/homepage.hamlet.cg index 34432b74..e8907860 100644 --- a/yesod/scaffold/hamlet/homepage.hamlet.cg +++ b/yesod/scaffold/hamlet/homepage.hamlet.cg @@ -1,2 +1,2 @@ -<h1>Hello +<h1>_{MsgHello} <h2 ##{h2id}>You do not have Javascript enabled. diff --git a/yesod/scaffold/messages/en.msg.cg b/yesod/scaffold/messages/en.msg.cg new file mode 100644 index 00000000..e928c34b --- /dev/null +++ b/yesod/scaffold/messages/en.msg.cg @@ -0,0 +1 @@ +Hello: Hello diff --git a/yesod/scaffold/tiny/Foundation.hs.cg b/yesod/scaffold/tiny/Foundation.hs.cg index d80e957e..ca02521b 100644 --- a/yesod/scaffold/tiny/Foundation.hs.cg +++ b/yesod/scaffold/tiny/Foundation.hs.cg @@ -1,8 +1,9 @@ {-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeFamilies #-} -{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE OverloadedStrings, MultiParamTypeClasses #-} module Foundation ( ~sitearg~ (..) , ~sitearg~Route (..) + , ~sitearg~Message (..) , resources~sitearg~ , Handler , Widget @@ -35,6 +36,9 @@ data ~sitearg~ = ~sitearg~ , getStatic :: Static -- ^ Settings for static file serving. } +-- Set up i18n messages. See the message folder. +mkMessage "~sitearg~" "messages" "en" + -- This is where we define all of the routes in our application. For a full -- explanation of the syntax, please see: -- http://docs.yesodweb.com/book/web-routes-quasi/ diff --git a/yesod/yesod.cabal b/yesod/yesod.cabal index 4e43b436..ef65d087 100644 --- a/yesod/yesod.cabal +++ b/yesod/yesod.cabal @@ -50,6 +50,7 @@ extra-source-files: scaffold/config/routes.cg scaffold/Settings.hs.cg scaffold/Settings/StaticFiles.hs.cg + scaffold/messages/en.msg.cg flag ghc7 From d77972ab601eac1ca1f7ba2498e4f878c590b534 Mon Sep 17 00:00:00 2001 From: Rune <rune@rune-90X3A.(none)> Date: Thu, 22 Sep 2011 09:21:00 +0200 Subject: [PATCH 43/44] added customErrorMessage to Yesod.Form.Functions to allow the user to overwrite the parse-error messages on fields. --- yesod-form/Yesod/Form/Functions.hs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/yesod-form/Yesod/Form/Functions.hs b/yesod-form/Yesod/Form/Functions.hs index b5910e4e..f762b621 100644 --- a/yesod-form/Yesod/Form/Functions.hs +++ b/yesod-form/Yesod/Form/Functions.hs @@ -31,6 +31,7 @@ module Yesod.Form.Functions , check , checkBool , checkM + , customErrorMessage ) where import Yesod.Form.Types @@ -309,3 +310,8 @@ checkM f field = field Right Nothing -> return $ Right Nothing Right (Just a) -> fmap (either (Left . SomeMessage) (Right . Just)) $ f a } + +-- | Allows you to overwrite the error message on parse error. +customErrorMessage :: SomeMessage master -> Field sub master a -> Field sub master a +customErrorMessage msg field = field { fieldParse = \ts -> fmap (either +(const $ Left msg) Right) $ fieldParse field ts } From 05ca4bc907706479342e2a941777cd524a080453 Mon Sep 17 00:00:00 2001 From: Michael Snoyman <michael@snoyman.com> Date: Thu, 22 Sep 2011 12:02:08 +0300 Subject: [PATCH 44/44] Proper disable-library-profiling --- yesod/Devel.hs | 4 ++-- yesod/main.hs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/yesod/Devel.hs b/yesod/Devel.hs index 03d8795f..09ce09aa 100755 --- a/yesod/Devel.hs +++ b/yesod/Devel.hs @@ -66,8 +66,8 @@ devel isDevel = do checkCabalFile gpd _ <- if isDevel - then rawSystem "cabal-dev" ["configure", "--cabal-install-arg=-fdevel"] - else rawSystem "cabal" ["configure", "-fdevel"] + then rawSystem "cabal-dev" ["configure", "--cabal-install-arg=-fdevel", "--disable-library-profiling"] + else rawSystem "cabal" ["configure", "-fdevel", "--disable-library-profiling"] T.writeFile "dist/devel.hs" (develFile pid) diff --git a/yesod/main.hs b/yesod/main.hs index a5719efa..988edfc6 100755 --- a/yesod/main.hs +++ b/yesod/main.hs @@ -36,7 +36,7 @@ main = do #endif ["devel"] -> devel isDev ["version"] -> putStrLn "0.9" - "configure":rest -> rawSystem cmd ("configure":"--disable-library-profiling":rest) >>= exitWith + "configure":rest -> rawSystem cmd ("configure":rest) >>= exitWith _ -> do putStrLn "Usage: yesod <command>" putStrLn "Available commands:"