yesod-bin 1.4.0.9 → 1.4.1
raw patch · 8 files changed
+2418/−2958 lines, 8 files
Files
- ChangeLog.md +13/−3
- hsfiles/mongo.hsfiles +400/−497
- hsfiles/mysql.hsfiles +410/−515
- hsfiles/postgres-fay.hsfiles +425/−523
- hsfiles/postgres.hsfiles +409/−514
- hsfiles/simple.hsfiles +353/−424
- hsfiles/sqlite.hsfiles +407/−481
- yesod-bin.cabal +1/−1
ChangeLog.md view
@@ -1,5 +1,15 @@-__1.4.0.9__ Allow devel.hs to be located in app/ or src/ subdirectories.+## 1.4.1 -__1.4.0.8__ Updated postgres-fay scaffolding for yesod-fay 0.7.0+Significant update to the scaffolding. -__1.4.0.7__ Fix a bug in `yesod devel` when cabal config has `tests: True` #864+## 1.4.0.9++Allow devel.hs to be located in app/ or src/ subdirectories.++## 1.4.0.8++Updated postgres-fay scaffolding for yesod-fay 0.7.0++## 1.4.0.7++Fix a bug in `yesod devel` when cabal config has `tests: True` #864
hsfiles/mongo.hsfiles view
@@ -28,30 +28,29 @@ {-# START_FILE Application.hs #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Application- ( makeApplication- , getApplicationDev+ ( getApplicationDev+ , appMain+ , develMain , makeFoundation ) where +import Control.Monad.Logger (liftLoc) import Import-import Settings-import Yesod.Auth-import Yesod.Default.Config-import Yesod.Default.Main-import Yesod.Default.Handlers-import Network.Wai.Middleware.RequestLogger- ( mkRequestLogger, outputFormat, OutputFormat (..), IPAddrSource (..), destination- )-import qualified Network.Wai.Middleware.RequestLogger as RequestLogger-import qualified Database.Persist-import Network.HTTP.Client.Conduit (newManager)-import System.Log.FastLogger (newStdoutLoggerSet, defaultBufSize)-import Network.Wai.Logger (clockDateCacher)-import Data.Default (def)-import Yesod.Core.Types (loggerSet, Logger (Logger))+import Language.Haskell.TH.Syntax (qLocation)+import Network.Wai.Handler.Warp (Settings, defaultSettings,+ defaultShouldDisplayException,+ runSettings, setHost,+ setOnException, setPort)+import Network.Wai.Middleware.RequestLogger (Destination (Logger),+ IPAddrSource (..),+ OutputFormat (..), destination,+ mkRequestLogger, outputFormat)+import System.Log.FastLogger (defaultBufSize, newStdoutLoggerSet,+ toLogStr) -- Import all relevant handler modules here. -- Don't forget to add new modules to your cabal file!+import Handler.Common import Handler.Home -- This line actually creates our YesodDispatch instance. It is the second half@@ -59,103 +58,122 @@ -- comments there for more details. mkYesodDispatch "App" resourcesApp --- This function allocates resources (such as a database connection pool),--- performs initialization and creates a WAI application. This is also the--- place to put your migrate statements to have automatic database+-- | This function allocates resources (such as a database connection pool),+-- performs initialization and return a foundation datatype value. This is also+-- the place to put your migrate statements to have automatic database -- migrations handled by Yesod.-makeApplication :: AppConfig DefaultEnv Extra -> IO (Application, LogFunc)-makeApplication conf = do- foundation <- makeFoundation conf+makeFoundation :: AppSettings -> IO App+makeFoundation appSettings = do+ -- Some basic initializations: HTTP connection manager, logger, and static+ -- subsite.+ appHttpManager <- newManager+ appLogger <- newStdoutLoggerSet defaultBufSize >>= makeYesodLogger+ appStatic <-+ (if appMutableStatic appSettings then staticDevel else static)+ (appStaticDir appSettings) - -- Initialize the logging middleware+ -- Create the database connection pool+ appConnPool <- createPoolConfig $ appDatabaseConf appSettings++ -- Return the foundation+ return App {..}++-- | Convert our foundation to a WAI Application by calling @toWaiAppPlain@ and+-- applyng some additional middlewares.+makeApplication :: App -> IO Application+makeApplication foundation = do logWare <- mkRequestLogger def { outputFormat =- if development+ if appDetailedRequestLogging $ appSettings foundation then Detailed True- else Apache FromSocket- , destination = RequestLogger.Logger $ loggerSet $ appLogger foundation+ else Apache+ (if appIpFromHeader $ appSettings foundation+ then FromFallback+ else FromSocket)+ , destination = Logger $ loggerSet $ appLogger foundation } -- Create the WAI application and apply middlewares- app <- toWaiAppPlain foundation- let logFunc = messageLoggerSource foundation (appLogger foundation)- return (logWare $ defaultMiddlewaresNoLogging app, logFunc)+ appPlain <- toWaiAppPlain foundation+ return $ logWare $ defaultMiddlewaresNoLogging appPlain --- | Loads up any necessary settings, creates your foundation datatype, and--- performs some initialization.-makeFoundation :: AppConfig DefaultEnv Extra -> IO App-makeFoundation conf = do- manager <- newManager- s <- staticSite- dbconf <- withYamlEnvironment "config/mongoDB.yml" (appEnv conf)- Database.Persist.loadConfig >>=- Database.Persist.applyEnv- p <- Database.Persist.createPoolConfig (dbconf :: Settings.PersistConf)+-- | Warp settings for the given foundation value.+warpSettings :: App -> Settings+warpSettings foundation =+ setPort (appPort $ appSettings foundation)+ $ setHost (appHost $ appSettings foundation)+ $ setOnException (\_req e ->+ when (defaultShouldDisplayException e) $ messageLoggerSource+ foundation+ (appLogger foundation)+ $(qLocation >>= liftLoc)+ "yesod"+ LevelError+ (toLogStr $ "Exception from Warp: " ++ show e))+ defaultSettings - loggerSet' <- newStdoutLoggerSet defaultBufSize- (getter, _) <- clockDateCacher+-- | For yesod devel, return the Warp settings and WAI Application.+getApplicationDev :: IO (Settings, Application)+getApplicationDev = do+ settings <- loadAppSettings [configSettingsYml] [] useEnv+ foundation <- makeFoundation settings+ app <- makeApplication foundation+ wsettings <- getDevSettings $ warpSettings foundation+ return (wsettings, app) - let logger = Yesod.Core.Types.Logger loggerSet' getter- foundation = App- { settings = conf- , getStatic = s- , connPool = p- , httpManager = manager- , persistConfig = dbconf- , appLogger = logger- }+-- | main function for use by yesod devel+develMain :: IO ()+develMain = develMainHelper getApplicationDev - return foundation+-- | The @main@ function for an executable running this site.+appMain :: IO ()+appMain = do+ -- Get the settings from all relevant sources+ settings <- loadAppSettingsArgs+ -- fall back to compile-time values, set to [] to require values at runtime+ [configSettingsYmlValue] --- for yesod devel-getApplicationDev :: IO (Int, Application)-getApplicationDev =- defaultDevelApp loader (fmap fst . makeApplication)- where- loader = Yesod.Default.Config.loadConfig (configSettings Development)- { csParseExtra = parseExtra- }+ -- allow environment variables to override+ useEnv + -- Generate the foundation from the settings+ foundation <- makeFoundation settings++ -- Generate a WAI Application from the foundation+ app <- makeApplication foundation++ -- Run the application with Warp+ runSettings (warpSettings foundation) app+ {-# START_FILE Foundation.hs #-} module Foundation where -import Prelude-import Yesod-import Yesod.Static-import Yesod.Auth-import Yesod.Auth.BrowserId-import Yesod.Default.Config-import Yesod.Default.Util (addStaticContentExternal)-import Network.HTTP.Client.Conduit (Manager, HasHttpManager (getHttpManager))-import qualified Settings-import Settings.Development (development)-import qualified Database.Persist-import Settings.StaticFiles+import ClassyPrelude.Yesod import Database.Persist.MongoDB hiding (master)-import Settings (widgetFile, Extra (..)) import Model-import Text.Jasmine (minifym)-import Text.Hamlet (hamletFile)-import Yesod.Core.Types (Logger)+import Settings+import Settings.StaticFiles+import Text.Hamlet (hamletFile)+import Text.Jasmine (minifym)+import Yesod.Auth+import Yesod.Auth.BrowserId (authBrowserId)+import Yesod.Core.Types (Logger)+import Yesod.Default.Util (addStaticContentExternal) --- | The site argument for your application. This can be a good place to+-- | The foundation datatype for your application. This can be a good place to -- keep settings and values requiring initialization before your application -- starts running, such as database connections. Every handler will have -- access to the data present here. data App = App- { settings :: AppConfig DefaultEnv Extra- , getStatic :: Static -- ^ Settings for static file serving.- , connPool :: Database.Persist.PersistConfigPool Settings.PersistConf -- ^ Database connection pool.- , httpManager :: Manager- , persistConfig :: Settings.PersistConf- , appLogger :: Logger+ { appSettings :: AppSettings+ , appStatic :: Static -- ^ Settings for static file serving.+ , appConnPool :: ConnectionPool -- ^ Database connection pool.+ , appHttpManager :: Manager+ , appLogger :: Logger } instance HasHttpManager App where- getHttpManager = httpManager---- Set up i18n messages. See the message folder.-mkMessage "App" "messages" "en"+ getHttpManager = appHttpManager -- This is where we define all of the routes in our application. For a full -- explanation of the syntax, please see:@@ -166,12 +184,15 @@ -- explanation for this split. mkYesodData "App" $(parseRoutesFile "config/routes") +-- | A convenient synonym for creating forms. type Form x = Html -> MForm (HandlerT App IO) (FormResult x, Widget) -- Please see the documentation for the Yesod typeclass. There are a number -- of settings which can be configured by overriding methods here. instance Yesod App where- approot = ApprootMaster $ appRoot . settings+ -- Controls the base of generated URLs. For more information on modifying,+ -- see: https://github.com/yesodweb/yesod/wiki/Overriding-approot+ approot = ApprootMaster $ appRoot . appSettings -- Store session data on the client in encrypted cookies, -- default session idle timeout is 120 minutes@@ -194,12 +215,6 @@ $(widgetFile "default-layout") withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet") - -- This is done to provide an optimization for serving static files from- -- a separate domain. Please see the staticRoot setting in Settings.hs- urlRenderOverride y (StaticR s) =- Just $ uncurry (joinPath y (Settings.staticRoot $ settings y)) $ renderRoute s- urlRenderOverride _ _ = Nothing- -- The page to be redirected to when authentication is required. authRoute _ = Just $ AuthR LoginR @@ -214,28 +229,39 @@ -- 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 =- addStaticContentExternal minifym genFileName Settings.staticDir (StaticR . flip StaticRoute [])+ addStaticContent ext mime content = do+ master <- getYesod+ let staticDir = appStaticDir $ appSettings master+ addStaticContentExternal+ minifym+ genFileName+ staticDir+ (StaticR . flip StaticRoute [])+ ext+ mime+ content where -- Generate a unique filename based on the content itself- genFileName lbs- | development = "autogen-" ++ base64md5 lbs- | otherwise = base64md5 lbs-- -- Place Javascript at bottom of the body tag so the rest of the page loads first- jsLoader _ = BottomOfBody+ genFileName lbs = "autogen-" ++ base64md5 lbs -- What messages should be logged. The following includes all messages when -- in development, and warnings and errors in production.- shouldLog _ _source level =- development || level == LevelWarn || level == LevelError+ shouldLog app _source level =+ appShouldLogAll (appSettings app)+ || level == LevelWarn+ || level == LevelError makeLogger = return . appLogger -- How to run database actions. instance YesodPersist App where type YesodPersistBackend App = MongoContext- runDB = defaultRunDB persistConfig connPool+ runDB action = do+ master <- getYesod+ runMongoDBPool+ (mgAccessMode $ appDatabaseConf $ appSettings master)+ action+ (appConnPool master) instance YesodAuth App where type AuthId App = UserId@@ -244,6 +270,8 @@ loginDest _ = HomeR -- Where to send a user after logout logoutDest _ = HomeR+ -- Override the above two destinations when a Referer: header is present+ redirectToReferer _ = True getAuthId creds = runDB $ do x <- getBy $ UniqueUser $ credsIdent creds@@ -258,7 +286,7 @@ -- You can add other plugins like BrowserID, email or OAuth here authPlugins _ = [authBrowserId def] - authHttpManager = httpManager+ authHttpManager = getHttpManager instance YesodAuthPersist App @@ -267,23 +295,38 @@ instance RenderMessage App FormMessage where renderMessage _ _ = defaultFormMessage --- | Get the 'Extra' value, used to hold data from the settings.yml file.-getExtra :: Handler Extra-getExtra = fmap (appExtra . settings) getYesod---- Note: previous versions of the scaffolding included a deliver function to--- send emails. Unfortunately, there are too many different options for us to--- give a reasonable default. Instead, the information is available on the--- wiki:+-- Note: Some functionality previously present in the scaffolding has been+-- moved to documentation in the Wiki. Following are some hopefully helpful+-- links: -- -- https://github.com/yesodweb/yesod/wiki/Sending-email+-- https://github.com/yesodweb/yesod/wiki/Serve-static-files-from-a-separate-domain+-- https://github.com/yesodweb/yesod/wiki/i18n-messages-in-the-scaffolding +{-# START_FILE Handler/Common.hs #-}+-- | Common handler functions.+module Handler.Common where++import Data.FileEmbed (embedFile)+import Import++-- These handlers embed files in the executable at compile time to avoid a+-- runtime dependency, and for efficiency.++getFaviconR :: Handler TypedContent+getFaviconR = return $ TypedContent "image/x-icon"+ $ toContent $(embedFile "config/favicon.ico")++getRobotsR :: Handler TypedContent+getRobotsR = return $ TypedContent typePlain+ $ toContent $(embedFile "config/robots.txt")+ {-# START_FILE Handler/Home.hs #-} module Handler.Home where import Import-import Yesod.Form.Bootstrap3- ( BootstrapFormLayout (..), renderBootstrap3, withSmallInput )+import Yesod.Form.Bootstrap3 (BootstrapFormLayout (..), renderBootstrap3,+ withSmallInput) -- This is a handler function for the GET request method on the HomeR -- resource pattern. All of your resource patterns are defined in@@ -325,42 +368,22 @@ ( module Import ) where -import Prelude as Import hiding (head, init, last,- readFile, tail, writeFile)-import Yesod as Import hiding (Route (..))--import Control.Applicative as Import (pure, (<$>), (<*>))-import Data.Text as Import (Text)--import Foundation as Import-import Model as Import-import Settings as Import-import Settings.Development as Import-import Settings.StaticFiles as Import--#if __GLASGOW_HASKELL__ >= 704-import Data.Monoid as Import- (Monoid (mappend, mempty, mconcat),- (<>))-#else-import Data.Monoid as Import- (Monoid (mappend, mempty, mconcat))--infixr 5 <>-(<>) :: Monoid m => m -> m -> m-(<>) = mappend-#endif+import ClassyPrelude.Yesod as Import+import Foundation as Import+import Model as Import+import Settings as Import+import Settings.StaticFiles as Import+import Yesod.Auth as Import+import Yesod.Core.Types as Import (loggerSet)+import Yesod.Default.Config2 as Import {-# START_FILE Model.hs #-} module Model where -import Yesod-import Data.Text (Text)+import ClassyPrelude.Yesod import Database.Persist.Quasi import Database.Persist.MongoDB hiding (master) import Language.Haskell.TH.Syntax-import Data.Typeable (Typeable)-import Prelude -- You can define all of your database entities in the entities file. -- You can find more information on persistent and how to declare entities@@ -391,7 +414,7 @@ Model Settings Settings.StaticFiles- Settings.Development+ Handler.Common Handler.Home if flag(dev) || flag(library-only)@@ -415,17 +438,21 @@ DeriveDataTypeable ViewPatterns TupleSections+ RecordWildCards build-depends: base >= 4 && < 5- , yesod >= 1.4.0 && < 1.5+ , yesod >= 1.4.1 && < 1.5 , yesod-core >= 1.4.0 && < 1.5 , yesod-auth >= 1.4.0 && < 1.5- , yesod-static >= 1.4.0 && < 1.5+ , yesod-static >= 1.4.0.3 && < 1.5 , yesod-form >= 1.4.0 && < 1.5+ , classy-prelude >= 0.10.2+ , classy-prelude-conduit >= 0.10.2+ , classy-prelude-yesod >= 0.10.2 , bytestring >= 0.9 && < 0.11 , text >= 0.11 && < 2.0 , persistent >= 2.0 && < 2.2- , persistent-mongoDB >= 2.0 && < 2.2+ , persistent-mongoDB >= 2.1.2 && < 2.2 , persistent-template >= 2.0 && < 2.2 , template-haskell , shakespeare >= 2.0 && < 2.1@@ -442,10 +469,12 @@ , monad-logger >= 0.3 && < 0.4 , fast-logger >= 2.2 && < 2.3 , wai-logger >= 2.2 && < 2.3-- -- see https://github.com/yesodweb/yesod/issues/814- if !os(windows)- build-depends: unix+ , file-embed+ , safe+ , unordered-containers+ , containers+ , vector+ , time executable PROJECTNAME if flag(library-only)@@ -453,9 +482,7 @@ main-is: main.hs hs-source-dirs: app- build-depends: base- , PROJECTNAME- , yesod+ build-depends: base, PROJECTNAME ghc-options: -threaded -O2 -rtsopts -with-rtsopts=-N @@ -483,7 +510,7 @@ build-depends: base , PROJECTNAME- , yesod-test >= 1.4 && < 1.5+ , yesod-test >= 1.4.2 && < 1.5 , yesod-core , yesod , persistent@@ -492,6 +519,8 @@ , monad-logger , transformers , hspec+ , classy-prelude+ , classy-prelude-yesod {-# START_FILE Settings.hs #-} -- | Settings are centralized, as much as possible, into this file. This@@ -501,45 +530,81 @@ -- declared in the Foundation.hs file. module Settings where -import Prelude-import Text.Shakespeare.Text (st)-import Language.Haskell.TH.Syntax-import Database.Persist.MongoDB (MongoConf)-import Yesod.Default.Config-import Yesod.Default.Util-import Data.Text (Text)-import Data.Yaml-import Control.Applicative-import Settings.Development-import Data.Default (def)-import Text.Hamlet+import ClassyPrelude.Yesod+import Control.Exception (throw)+import Data.Aeson (Result (..), fromJSON, withObject, (.!=),+ (.:?))+import Data.FileEmbed (embedFile)+import Data.Yaml (decodeEither')+import Database.Persist.MongoDB (MongoConf)+import Language.Haskell.TH.Syntax (Exp, Name, Q)+import Network.Wai.Handler.Warp (HostPreference)+import Yesod.Default.Config2 (applyEnvValue, configSettingsYml)+import Yesod.Default.Util (WidgetFileSettings, widgetFileNoReload,+ widgetFileReload) --- | Which Persistent backend this site is using.-type PersistConf = MongoConf+-- | Runtime settings to configure this application. These settings can be+-- loaded from various sources: defaults, environment variables, config files,+-- theoretically even a database.+data AppSettings = AppSettings+ { appStaticDir :: String+ -- ^ Directory from which to serve static files.+ , appDatabaseConf :: MongoConf+ -- ^ Configuration settings for accessing the database.+ , appRoot :: Text+ -- ^ Base for all generated URLs.+ , appHost :: HostPreference+ -- ^ Host/interface the server should bind to.+ , appPort :: Int+ -- ^ Port to listen on+ , appIpFromHeader :: Bool+ -- ^ Get the IP address from the header when logging. Useful when sitting+ -- behind a reverse proxy. --- Static setting below. Changing these requires a recompile+ , appDetailedRequestLogging :: Bool+ -- ^ Use detailed request logging system+ , appShouldLogAll :: Bool+ -- ^ Should all log messages be displayed?+ , appReloadTemplates :: Bool+ -- ^ Use the reload version of templates+ , appMutableStatic :: Bool+ -- ^ Assume that files in the static dir may change after compilation+ , appSkipCombining :: Bool+ -- ^ Perform no stylesheet/script combining --- | 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-staticDir = "static"+ -- Example app-specific configuration values.+ , appCopyright :: Text+ -- ^ Copyright text to appear in the footer of the page+ , appAnalytics :: Maybe Text+ -- ^ Google Analytics code+ } --- | The base URL for your static files. As you can see by the default--- value, this can simply be "static" appended to your application root.--- A powerful optimization can be serving static files from a separate--- domain name. This allows you to use a web server optimized for static--- files, more easily set expires and cache values, and avoid possibly--- costly transference of cookies on static files. For more information,--- please see:--- http://code.google.com/speed/page-speed/docs/request.html#ServeFromCookielessDomain------ If you change the resource pattern for StaticR in Foundation.hs, you will--- have to make a corresponding change here.------ To see how this value is used, see urlRenderOverride in Foundation.hs-staticRoot :: AppConfig DefaultEnv x -> Text-staticRoot conf = [st|#{appRoot conf}/static|]+instance FromJSON AppSettings where+ parseJSON = withObject "AppSettings" $ \o -> do+ let defaultDev =+#if DEVELOPMENT+ True+#else+ False+#endif+ appStaticDir <- o .: "static-dir"+ appDatabaseConf <- o .: "database"+ appRoot <- o .: "approot"+ appHost <- fromString <$> o .: "host"+ appPort <- o .: "port"+ appIpFromHeader <- o .: "ip-from-header" + appDetailedRequestLogging <- o .:? "detailed-logging" .!= defaultDev+ appShouldLogAll <- o .:? "should-log-all" .!= defaultDev+ appReloadTemplates <- o .:? "reload-templates" .!= defaultDev+ appMutableStatic <- o .:? "mutable-static" .!= defaultDev+ appSkipCombining <- o .:? "skip-combining" .!= defaultDev++ appCopyright <- o .: "copyright"+ appAnalytics <- o .:? "analytics"++ return AppSettings {..}+ -- | Settings for 'widgetFile', such as which template languages to support and -- default Hamlet settings. --@@ -548,81 +613,62 @@ -- https://github.com/yesodweb/yesod/wiki/Overriding-widgetFile widgetFileSettings :: WidgetFileSettings widgetFileSettings = def- { wfsHamletSettings = defaultHamletSettings- { hamletNewlines = AlwaysNewlines- }- } +-- | How static files should be combined.+combineSettings :: CombineSettings+combineSettings = def+ -- The rest of this file contains settings which rarely need changing by a -- user. widgetFile :: String -> Q Exp-widgetFile = (if development then widgetFileReload- else widgetFileNoReload)+widgetFile = (if appReloadTemplates compileTimeAppSettings+ then widgetFileReload+ else widgetFileNoReload) widgetFileSettings -data Extra = Extra- { extraCopyright :: Text- , extraAnalytics :: Maybe Text -- ^ Google Analytics- } deriving Show+-- | Raw bytes at compile time of @config/settings.yml@+configSettingsYmlBS :: ByteString+configSettingsYmlBS = $(embedFile configSettingsYml) -parseExtra :: DefaultEnv -> Object -> Parser Extra-parseExtra _ o = Extra- <$> o .: "copyright"- <*> o .:? "analytics"+-- | @config/settings.yml@, parsed to a @Value@.+configSettingsYmlValue :: Value+configSettingsYmlValue = either throw id $ decodeEither' configSettingsYmlBS -{-# START_FILE Settings/Development.hs #-}-module Settings.Development where+-- | A version of @AppSettings@ parsed at compile time from @config/settings.yml@.+compileTimeAppSettings :: AppSettings+compileTimeAppSettings =+ case fromJSON $ applyEnvValue False mempty configSettingsYmlValue of+ Error e -> error e+ Success settings -> settings -import Prelude+-- The following two functions can be used to combine multiple CSS or JS files+-- at compile time to decrease the number of http requests.+-- Sample usage (inside a Widget):+--+-- > $(combineStylesheets 'StaticR [style1_css, style2_css]) -development :: Bool-development =-#if DEVELOPMENT- True-#else- False-#endif+combineStylesheets :: Name -> [Route Static] -> Q Exp+combineStylesheets = combineStylesheets'+ (appSkipCombining compileTimeAppSettings)+ combineSettings -production :: Bool-production = not development+combineScripts :: Name -> [Route Static] -> Q Exp+combineScripts = combineScripts'+ (appSkipCombining compileTimeAppSettings)+ combineSettings {-# START_FILE Settings/StaticFiles.hs #-} module Settings.StaticFiles where -import Prelude (IO)-import Yesod.Static-import qualified Yesod.Static as Static-import Settings (staticDir)-import Settings.Development-import Language.Haskell.TH (Q, Exp, Name)-import Data.Default (def)---- | use this to create your static file serving site-staticSite :: IO Static.Static-staticSite = if development then Static.staticDevel staticDir- else Static.static staticDir+import Settings (appStaticDir, compileTimeAppSettings)+import Yesod.Static (staticFiles) -- | This generates easy references to files in the static directory at compile time, -- giving you compile-time verification that referenced files exist. -- Warning: any files added to your static directory during run-time can't be -- accessed this way. You'll have to use their FilePath or URL to access them.-$(staticFiles Settings.staticDir)--combineSettings :: CombineSettings-combineSettings = def---- The following two functions can be used to combine multiple CSS or JS files--- at compile time to decrease the number of http requests.--- Sample usage (inside a Widget):------ > $(combineStylesheets 'StaticR [style1_css, style2_css])--combineStylesheets :: Name -> [Route Static] -> Q Exp-combineStylesheets = combineStylesheets' development combineSettings--combineScripts :: Name -> [Route Static] -> Q Exp-combineScripts = combineScripts' development combineSettings+staticFiles (appStaticDir compileTimeAppSettings) {-# START_FILE app/DevelMain.hs #-} -- | Development version to be run inside GHCi.@@ -689,19 +735,24 @@ start :: MVar () -- ^ Written to when the thread is killed. -> IO ThreadId start done = do- (port,app) <- getApplicationDev- forkIO (finally (runSettings (setPort port defaultSettings) app)+ (settings,app) <- getApplicationDev+ forkIO (finally (runSettings settings app) (putMVar done ())) +{-# START_FILE app/devel.hs #-}+{-# LANGUAGE PackageImports #-}+import "PROJECTNAME" Application (develMain)+import Prelude (IO)++main :: IO ()+main = develMain+ {-# START_FILE app/main.hs #-}-import Prelude (IO)-import Yesod.Default.Config (fromArgs)-import Yesod.Default.Main (defaultMainLog)-import Settings (parseExtra)-import Application (makeApplication)+import Prelude (IO)+import Application (appMain) main :: IO ()-main = defaultMainLog (fromArgs parseExtra) makeApplication+main = appMain {-# START_FILE BASE64 config/favicon.ico #-} AAABAAIAEBAAAAEAIABoBAAAJgAAABAQAgABAAEAsAAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAA@@ -806,37 +857,11 @@ -- By default this file is used in Model.hs (which is imported by Foundation.hs) -{-# START_FILE config/mongoDB.yml #-}-Default: &defaults- user: PROJECTNAME- password: PROJECTNAME- host: localhost- database: PROJECTNAME- connections: 10--Development:- <<: *defaults--Testing:- database: PROJECTNAME_test- <<: *defaults--Staging:- database: PROJECTNAME_staging- connections: 100- <<: *defaults--Production:- database: PROJECTNAME_production- connections: 100- host: localhost- <<: *defaults- {-# START_FILE config/robots.txt #-} User-agent: * {-# START_FILE config/routes #-}-/static StaticR Static getStatic+/static StaticR Static appStatic /auth AuthR Auth getAuth /favicon.ico FaviconR GET@@ -845,155 +870,35 @@ / HomeR GET POST {-# START_FILE config/settings.yml #-}-Default: &defaults- host: "*4" # any IPv4 host- port: 3000- approot: "http://localhost:3000"- copyright: Insert copyright statement here- #analytics: UA-YOURCODE--Development:- <<: *defaults--Testing:- <<: *defaults--Staging:- <<: *defaults--Production:- #approot: "http://www.example.com"- <<: *defaults--{-# START_FILE deploy/Procfile #-}-# Free deployment to Heroku.-#-# !! Warning: You must use a 64 bit machine to compile !!-#-# This could mean using a virtual machine. Give your VM as much memory as you can to speed up linking.-#-# Basic Yesod setup:-#-# * Move this file out of the deploy directory and into your root directory-#-# mv deploy/Procfile ./-#-# * Create an empty package.json-# echo '{ "name": "PROJECTNAME", "version": "0.0.1", "dependencies": {} }' >> package.json-#-# Postgresql Yesod setup:-#-# * add dependencies on the "heroku", "aeson" and "unordered-containers" packages in your cabal file-#-# * add code in Application.hs to use the heroku package and load the connection parameters.-# The below works for Postgresql.-#-# import Data.HashMap.Strict as H-# import Data.Aeson.Types as AT-# #ifndef DEVELOPMENT-# import qualified Web.Heroku-# #endif-#-#-#-# makeFoundation :: AppConfig DefaultEnv Extra -> Logger -> IO App-# makeFoundation conf setLogger = do-# manager <- newManager def-# s <- staticSite-# hconfig <- loadHerokuConfig-# dbconf <- withYamlEnvironment "config/postgresql.yml" (appEnv conf)-# (Database.Persist.Store.loadConfig . combineMappings hconfig) >>=-# Database.Persist.Store.applyEnv-# p <- Database.Persist.Store.createPoolConfig (dbconf :: Settings.PersistConfig)-# Database.Persist.Store.runPool dbconf (runMigration migrateAll) p-# return $ App conf setLogger s p manager dbconf-#-# #ifndef DEVELOPMENT-# canonicalizeKey :: (Text, val) -> (Text, val)-# canonicalizeKey ("dbname", val) = ("database", val)-# canonicalizeKey pair = pair-#-# toMapping :: [(Text, Text)] -> AT.Value-# toMapping xs = AT.Object $ M.fromList $ map (\(key, val) -> (key, AT.String val)) xs-# #endif-#-# combineMappings :: AT.Value -> AT.Value -> AT.Value-# combineMappings (AT.Object m1) (AT.Object m2) = AT.Object $ m1 `M.union` m2-# combineMappings _ _ = error "Data.Object is not a Mapping."-#-# loadHerokuConfig :: IO AT.Value-# loadHerokuConfig = do-# #ifdef DEVELOPMENT-# return $ AT.Object M.empty-# #else-# Web.Heroku.dbConnParams >>= return . toMapping . map canonicalizeKey-# #endif--+static-dir: "_env:STATIC_DIR:static"+host: "_env:HOST:*4" # any IPv4 host+port: "_env:PORT:3000"+approot: "_env:APPROOT:http://localhost:3000"+ip-from-header: "_env:IP_FROM_HEADER:false" -# Heroku setup:-# Find the Heroku guide. Roughly:-#-# * sign up for a heroku account and register your ssh key-# * create a new application on the *cedar* stack-#-# * make your Yesod project the git repository for that application-# * create a deploy branch-#-# git checkout -b deploy-#-# Repeat these steps to deploy:-# * add your web executable binary (referenced below) to the git repository-#-# git checkout deploy-# git add ./dist/build/PROJECTNAME/PROJECTNAME-# git commit -m deploy-#-# * push to Heroku+# Optional values with the following production defaults.+# In development, they default to the inverse. #-# git push heroku deploy:master---# Heroku configuration that runs your app-web: ./dist/build/PROJECTNAME/PROJECTNAME production -p $PORT--{-# START_FILE devel.hs #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE PackageImports #-}-import "PROJECTNAME" Application (getApplicationDev)-import Network.Wai.Handler.Warp- (runSettings, defaultSettings, setPort)-import Control.Concurrent (forkIO)-import System.Directory (doesFileExist, removeFile)-import System.Exit (exitSuccess)-import Control.Concurrent (threadDelay)--#ifndef mingw32_HOST_OS-import System.Posix.Signals (installHandler, sigINT, Handler(Catch))-#endif--main :: IO ()-main = do-#ifndef mingw32_HOST_OS- _ <- installHandler sigINT (Catch $ return ()) Nothing-#endif-- putStrLn "Starting devel application"- (port, app) <- getApplicationDev- forkIO $ runSettings (setPort port defaultSettings) app- loop+# development: false+# detailed-logging: false+# should-log-all: false+# reload-templates: false+# mutable-static: false+# skip-combining: false -loop :: IO ()-loop = do- threadDelay 100000- e <- doesFileExist "yesod-devel/devel-terminate"- if e then terminateDevel else loop+database:+ user: "PROJECTNAME"+ password: "PROJECTNAME"+ host: "localhost"+ database: "PROJECTNAME"+ connections: 10 -terminateDevel :: IO ()-terminateDevel = exitSuccess+copyright: Insert copyright statement here+#analytics: UA-YOURCODE -{-# START_FILE messages/en.msg #-}-Hello: Hello+{-# START_FILE config/test-settings.yml #-}+database:+ database: PROJECTNAME_test {-# START_FILE static/css/bootstrap.css #-} /*!@@ -8959,9 +8864,9 @@ <div id="main" role="main"> ^{pageBody pc} <footer>- #{extraCopyright $ appExtra $ settings master}+ #{appCopyright $ appSettings master} - $maybe analytics <- extraAnalytics $ appExtra $ settings master+ $maybe analytics <- appAnalytics $ appSettings master <script> if(!window.location.href.match(/localhost/)){ window._gaq = [['_setAccount','#{analytics}'],['_trackPageview'],['_trackPageLoadTime']];@@ -8984,7 +8889,7 @@ ^{widget} {-# START_FILE templates/homepage.hamlet #-}-<h1>_{MsgHello}+<h1>Welcome to Yesod! <ol> <li>Now that you have a working project you should use the #@@ -9035,94 +8940,92 @@ color: #990 } -{-# START_FILE test/Handler/HomeSpec.hs #-}-module Handler.HomeSpec- ( spec- ) where+{-# START_FILE test/Handler/CommonSpec.hs #-}+module Handler.CommonSpec (spec) where import TestImport-import qualified Data.List as L spec :: Spec-spec =- ydescribe "These are some example tests" $ do-- yit "loads the index and checks it looks right" $ do- get HomeR+spec = withApp $ do+ describe "robots.txt" $ do+ it "gives a 200" $ do+ get RobotsR statusIs 200- htmlAllContain "h1" "Hello"+ it "has correct User-agent" $ do+ get RobotsR+ bodyContains "User-agent: *"+ describe "favicon.ico" $ do+ it "gives a 200" $ do+ get FaviconR+ statusIs 200 - request $ do- setMethod "POST"- setUrl HomeR- addNonce- fileByLabel "Choose a file" "test/Spec.hs" "text/plain" -- talk about self-reference- byLabel "What's on the file?" "Some Content"+{-# START_FILE test/Handler/HomeSpec.hs #-}+module Handler.HomeSpec (spec) where - statusIs 200- -- more debugging printBody- htmlCount ".message" 1- htmlAllContain ".message" "Some Content"- htmlAllContain ".message" "text/plain"+import TestImport - -- This is a simple example of using a database access in a test. The- -- test will succeed for a fresh scaffolded site with an empty database,- -- but will fail on an existing database with a non-empty user table.- yit "leaves the user table empty" $ do- get HomeR- statusIs 200- users <- runDB $ selectList ([] :: [Filter User]) []- assertEqual "user table empty" 0 $ L.length users+spec :: Spec+spec = withApp $ do+ it "loads the index and checks it looks right" $ do+ get HomeR+ statusIs 200+ htmlAllContain "h1" "Welcome to Yesod" -{-# START_FILE test/Spec.hs #-}-module Main where+ request $ do+ setMethod "POST"+ setUrl HomeR+ addNonce+ fileByLabel "Choose a file" "test/Spec.hs" "text/plain" -- talk about self-reference+ byLabel "What's on the file?" "Some Content" -import Import-import Yesod.Default.Config-import Yesod.Test-import Test.Hspec (hspec)-import Application (makeFoundation)+ statusIs 200+ -- more debugging printBody+ htmlCount ".message" 1+ htmlAllContain ".message" "Some Content"+ htmlAllContain ".message" "text/plain" -import qualified Handler.HomeSpec+ -- This is a simple example of using a database access in a test. The+ -- test will succeed for a fresh scaffolded site with an empty database,+ -- but will fail on an existing database with a non-empty user table.+ it "leaves the user table empty" $ do+ get HomeR+ statusIs 200+ users <- runDB $ selectList ([] :: [Filter User]) []+ assertEqual "user table empty" 0 $ length users -main :: IO ()-main = do- conf <- Yesod.Default.Config.loadConfig $ (configSettings Testing)- { csParseExtra = parseExtra- }- foundation <- makeFoundation conf- hspec $ do- yesodSpec foundation $ do- Handler.HomeSpec.spec+{-# START_FILE test/Spec.hs #-}+{-# OPTIONS_GHC -F -pgmF hspec-discover #-} {-# START_FILE test/TestImport.hs #-} module TestImport- ( module Yesod.Test- , module Model- , module Foundation- , module Database.Persist- , module Prelude- , runDB- , Spec- , Example+ ( module TestImport+ , module X ) where -import Yesod.Test-import Database.Persist hiding (get)+import Application (makeFoundation)+import ClassyPrelude as X+import Database.Persist as X hiding (get) import Database.Persist.MongoDB hiding (master)-import Control.Monad.Trans.Resource (ResourceT, runResourceT)-import Control.Monad.Logger (NoLoggingT, runNoLoggingT)-import Control.Monad.IO.Class (liftIO)-import Prelude--import Foundation-import Model+import Foundation as X+import Model as X+import Settings (appDatabaseConf)+import Test.Hspec as X+import Yesod.Default.Config2 (ignoreEnv, loadAppSettings)+import Yesod.Test as X -type Spec = YesodSpec App-type Example = YesodExample App+runDB :: Action IO a -> YesodExample App a+runDB action = do+ master <- getTestYesod+ liftIO $ runMongoDBPool+ (mgAccessMode $ appDatabaseConf $ appSettings master)+ action+ (appConnPool master) -runDB :: Action (NoLoggingT (ResourceT IO)) a -> YesodExample App a-runDB query = do- pool <- fmap connPool getTestYesod- liftIO $ runResourceT $ runNoLoggingT $ runMongoDBPoolDef query pool+withApp :: SpecWith App -> Spec+withApp = before $ do+ settings <- loadAppSettings+ ["config/test-settings.yml", "config/settings.yml"]+ []+ ignoreEnv+ makeFoundation settings
hsfiles/mysql.hsfiles view
@@ -28,32 +28,31 @@ {-# START_FILE Application.hs #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Application- ( makeApplication- , getApplicationDev+ ( getApplicationDev+ , appMain+ , develMain , makeFoundation ) where +import Control.Monad.Logger (liftLoc, runLoggingT)+import Database.Persist.MySQL (createMySQLPool, myConnInfo,+ myPoolSize, runSqlPool) import Import-import Yesod.Auth-import Yesod.Default.Config-import Yesod.Default.Main-import Yesod.Default.Handlers-import Network.Wai.Middleware.RequestLogger- ( mkRequestLogger, outputFormat, OutputFormat (..), IPAddrSource (..), destination- )-import qualified Network.Wai.Middleware.RequestLogger as RequestLogger-import qualified Database.Persist-import Database.Persist.Sql (runMigration)-import Database.Persist.MySQL (createMySQLPool, myConnInfo, myPoolSize)-import Network.HTTP.Client.Conduit (newManager)-import Control.Monad.Logger (runLoggingT)-import System.Log.FastLogger (newStdoutLoggerSet, defaultBufSize)-import Network.Wai.Logger (clockDateCacher)-import Data.Default (def)-import Yesod.Core.Types (loggerSet, Logger (Logger))+import Language.Haskell.TH.Syntax (qLocation)+import Network.Wai.Handler.Warp (Settings, defaultSettings,+ defaultShouldDisplayException,+ runSettings, setHost,+ setOnException, setPort)+import Network.Wai.Middleware.RequestLogger (Destination (Logger),+ IPAddrSource (..),+ OutputFormat (..), destination,+ mkRequestLogger, outputFormat)+import System.Log.FastLogger (defaultBufSize, newStdoutLoggerSet,+ toLogStr) -- Import all relevant handler modules here. -- Don't forget to add new modules to your cabal file!+import Handler.Common import Handler.Home -- This line actually creates our YesodDispatch instance. It is the second half@@ -61,112 +60,136 @@ -- comments there for more details. mkYesodDispatch "App" resourcesApp --- This function allocates resources (such as a database connection pool),--- performs initialization and creates a WAI application. This is also the--- place to put your migrate statements to have automatic database+-- | This function allocates resources (such as a database connection pool),+-- performs initialization and return a foundation datatype value. This is also+-- the place to put your migrate statements to have automatic database -- migrations handled by Yesod.-makeApplication :: AppConfig DefaultEnv Extra -> IO (Application, LogFunc)-makeApplication conf = do- foundation <- makeFoundation conf+makeFoundation :: AppSettings -> IO App+makeFoundation appSettings = do+ -- Some basic initializations: HTTP connection manager, logger, and static+ -- subsite.+ appHttpManager <- newManager+ appLogger <- newStdoutLoggerSet defaultBufSize >>= makeYesodLogger+ appStatic <-+ (if appMutableStatic appSettings then staticDevel else static)+ (appStaticDir appSettings) - -- Initialize the logging middleware+ -- We need a log function to create a connection pool. We need a connection+ -- pool to create our foundation. And we need our foundation to get a+ -- logging function. To get out of this loop, we initially create a+ -- temporary foundation without a real connection pool, get a log function+ -- from there, and then create the real foundation.+ let mkFoundation appConnPool = App {..}+ tempFoundation = mkFoundation $ error "connPool forced in tempFoundation"+ logFunc = messageLoggerSource tempFoundation appLogger++ -- Create the database connection pool+ pool <- flip runLoggingT logFunc $ createMySQLPool+ (myConnInfo $ appDatabaseConf appSettings)+ (myPoolSize $ appDatabaseConf appSettings)++ -- Perform database migration using our application's logging settings.+ runLoggingT (runSqlPool (runMigration migrateAll) pool) logFunc++ -- Return the foundation+ return $ mkFoundation pool++-- | Convert our foundation to a WAI Application by calling @toWaiAppPlain@ and+-- applyng some additional middlewares.+makeApplication :: App -> IO Application+makeApplication foundation = do logWare <- mkRequestLogger def { outputFormat =- if development+ if appDetailedRequestLogging $ appSettings foundation then Detailed True- else Apache FromSocket- , destination = RequestLogger.Logger $ loggerSet $ appLogger foundation+ else Apache+ (if appIpFromHeader $ appSettings foundation+ then FromFallback+ else FromSocket)+ , destination = Logger $ loggerSet $ appLogger foundation } -- Create the WAI application and apply middlewares- app <- toWaiAppPlain foundation- let logFunc = messageLoggerSource foundation (appLogger foundation)- return (logWare $ defaultMiddlewaresNoLogging app, logFunc)+ appPlain <- toWaiAppPlain foundation+ return $ logWare $ defaultMiddlewaresNoLogging appPlain --- | Loads up any necessary settings, creates your foundation datatype, and--- performs some initialization.-makeFoundation :: AppConfig DefaultEnv Extra -> IO App-makeFoundation conf = do- manager <- newManager- s <- staticSite- dbconf <- withYamlEnvironment "config/mysql.yml" (appEnv conf)- Database.Persist.loadConfig >>=- Database.Persist.applyEnv+-- | Warp settings for the given foundation value.+warpSettings :: App -> Settings+warpSettings foundation =+ setPort (appPort $ appSettings foundation)+ $ setHost (appHost $ appSettings foundation)+ $ setOnException (\_req e ->+ when (defaultShouldDisplayException e) $ messageLoggerSource+ foundation+ (appLogger foundation)+ $(qLocation >>= liftLoc)+ "yesod"+ LevelError+ (toLogStr $ "Exception from Warp: " ++ show e))+ defaultSettings - loggerSet' <- newStdoutLoggerSet defaultBufSize- (getter, _) <- clockDateCacher+-- | For yesod devel, return the Warp settings and WAI Application.+getApplicationDev :: IO (Settings, Application)+getApplicationDev = do+ settings <- loadAppSettings [configSettingsYml] [] useEnv+ foundation <- makeFoundation settings+ app <- makeApplication foundation+ wsettings <- getDevSettings $ warpSettings foundation+ return (wsettings, app) - let logger = Yesod.Core.Types.Logger loggerSet' getter- mkFoundation p = App- { settings = conf- , getStatic = s- , connPool = p- , httpManager = manager- , persistConfig = dbconf- , appLogger = logger- }- tempFoundation = mkFoundation $ error "connPool forced in tempFoundation"- logFunc = messageLoggerSource tempFoundation logger+-- | main function for use by yesod devel+develMain :: IO ()+develMain = develMainHelper getApplicationDev - p <- flip runLoggingT logFunc- $ createMySQLPool (myConnInfo dbconf) (myPoolSize dbconf)- let foundation = mkFoundation p+-- | The @main@ function for an executable running this site.+appMain :: IO ()+appMain = do+ -- Get the settings from all relevant sources+ settings <- loadAppSettingsArgs+ -- fall back to compile-time values, set to [] to require values at runtime+ [configSettingsYmlValue] - -- Perform database migration using our application's logging settings.- flip runLoggingT logFunc- (Database.Persist.runPool dbconf (runMigration migrateAll) p)+ -- allow environment variables to override+ useEnv - return foundation+ -- Generate the foundation from the settings+ foundation <- makeFoundation settings --- for yesod devel-getApplicationDev :: IO (Int, Application)-getApplicationDev =- defaultDevelApp loader (fmap fst . makeApplication)- where- loader = Yesod.Default.Config.loadConfig (configSettings Development)- { csParseExtra = parseExtra- }+ -- Generate a WAI Application from the foundation+ app <- makeApplication foundation + -- Run the application with Warp+ runSettings (warpSettings foundation) app+ {-# START_FILE Foundation.hs #-} module Foundation where -import Prelude-import Yesod-import Yesod.Static-import Yesod.Auth-import Yesod.Auth.BrowserId-import Yesod.Default.Config-import Yesod.Default.Util (addStaticContentExternal)-import Network.HTTP.Client.Conduit (Manager, HasHttpManager (getHttpManager))-import qualified Settings-import Settings.Development (development)-import qualified Database.Persist-import Database.Persist.Sql (SqlBackend)-import Settings.StaticFiles-import Settings (widgetFile, Extra (..))+import ClassyPrelude.Yesod+import Database.Persist.Sql (ConnectionPool, runSqlPool) import Model-import Text.Jasmine (minifym)-import Text.Hamlet (hamletFile)-import Yesod.Core.Types (Logger)+import Settings+import Settings.StaticFiles+import Text.Hamlet (hamletFile)+import Text.Jasmine (minifym)+import Yesod.Auth+import Yesod.Auth.BrowserId (authBrowserId)+import Yesod.Core.Types (Logger)+import Yesod.Default.Util (addStaticContentExternal) --- | The site argument for your application. This can be a good place to+-- | The foundation datatype for your application. This can be a good place to -- keep settings and values requiring initialization before your application -- starts running, such as database connections. Every handler will have -- access to the data present here. data App = App- { settings :: AppConfig DefaultEnv Extra- , getStatic :: Static -- ^ Settings for static file serving.- , connPool :: Database.Persist.PersistConfigPool Settings.PersistConf -- ^ Database connection pool.- , httpManager :: Manager- , persistConfig :: Settings.PersistConf- , appLogger :: Logger+ { appSettings :: AppSettings+ , appStatic :: Static -- ^ Settings for static file serving.+ , appConnPool :: ConnectionPool -- ^ Database connection pool.+ , appHttpManager :: Manager+ , appLogger :: Logger } instance HasHttpManager App where- getHttpManager = httpManager---- Set up i18n messages. See the message folder.-mkMessage "App" "messages" "en"+ getHttpManager = appHttpManager -- This is where we define all of the routes in our application. For a full -- explanation of the syntax, please see:@@ -177,12 +200,15 @@ -- explanation for this split. mkYesodData "App" $(parseRoutesFile "config/routes") +-- | A convenient synonym for creating forms. type Form x = Html -> MForm (HandlerT App IO) (FormResult x, Widget) -- Please see the documentation for the Yesod typeclass. There are a number -- of settings which can be configured by overriding methods here. instance Yesod App where- approot = ApprootMaster $ appRoot . settings+ -- Controls the base of generated URLs. For more information on modifying,+ -- see: https://github.com/yesodweb/yesod/wiki/Overriding-approot+ approot = ApprootMaster $ appRoot . appSettings -- Store session data on the client in encrypted cookies, -- default session idle timeout is 120 minutes@@ -205,12 +231,6 @@ $(widgetFile "default-layout") withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet") - -- This is done to provide an optimization for serving static files from- -- a separate domain. Please see the staticRoot setting in Settings.hs- urlRenderOverride y (StaticR s) =- Just $ uncurry (joinPath y (Settings.staticRoot $ settings y)) $ renderRoute s- urlRenderOverride _ _ = Nothing- -- The page to be redirected to when authentication is required. authRoute _ = Just $ AuthR LoginR @@ -225,30 +245,38 @@ -- 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 =- addStaticContentExternal minifym genFileName Settings.staticDir (StaticR . flip StaticRoute [])+ addStaticContent ext mime content = do+ master <- getYesod+ let staticDir = appStaticDir $ appSettings master+ addStaticContentExternal+ minifym+ genFileName+ staticDir+ (StaticR . flip StaticRoute [])+ ext+ mime+ content where -- Generate a unique filename based on the content itself- genFileName lbs- | development = "autogen-" ++ base64md5 lbs- | otherwise = base64md5 lbs-- -- Place Javascript at bottom of the body tag so the rest of the page loads first- jsLoader _ = BottomOfBody+ genFileName lbs = "autogen-" ++ base64md5 lbs -- What messages should be logged. The following includes all messages when -- in development, and warnings and errors in production.- shouldLog _ _source level =- development || level == LevelWarn || level == LevelError+ shouldLog app _source level =+ appShouldLogAll (appSettings app)+ || level == LevelWarn+ || level == LevelError makeLogger = return . appLogger -- How to run database actions. instance YesodPersist App where type YesodPersistBackend App = SqlBackend- runDB = defaultRunDB persistConfig connPool+ runDB action = do+ master <- getYesod+ runSqlPool action $ appConnPool master instance YesodPersistRunner App where- getDBRunner = defaultGetDBRunner connPool+ getDBRunner = defaultGetDBRunner appConnPool instance YesodAuth App where type AuthId App = UserId@@ -257,6 +285,8 @@ loginDest _ = HomeR -- Where to send a user after logout logoutDest _ = HomeR+ -- Override the above two destinations when a Referer: header is present+ redirectToReferer _ = True getAuthId creds = runDB $ do x <- getBy $ UniqueUser $ credsIdent creds@@ -271,7 +301,7 @@ -- You can add other plugins like BrowserID, email or OAuth here authPlugins _ = [authBrowserId def] - authHttpManager = httpManager+ authHttpManager = getHttpManager instance YesodAuthPersist App @@ -280,23 +310,38 @@ instance RenderMessage App FormMessage where renderMessage _ _ = defaultFormMessage --- | Get the 'Extra' value, used to hold data from the settings.yml file.-getExtra :: Handler Extra-getExtra = fmap (appExtra . settings) getYesod---- Note: previous versions of the scaffolding included a deliver function to--- send emails. Unfortunately, there are too many different options for us to--- give a reasonable default. Instead, the information is available on the--- wiki:+-- Note: Some functionality previously present in the scaffolding has been+-- moved to documentation in the Wiki. Following are some hopefully helpful+-- links: -- -- https://github.com/yesodweb/yesod/wiki/Sending-email+-- https://github.com/yesodweb/yesod/wiki/Serve-static-files-from-a-separate-domain+-- https://github.com/yesodweb/yesod/wiki/i18n-messages-in-the-scaffolding +{-# START_FILE Handler/Common.hs #-}+-- | Common handler functions.+module Handler.Common where++import Data.FileEmbed (embedFile)+import Import++-- These handlers embed files in the executable at compile time to avoid a+-- runtime dependency, and for efficiency.++getFaviconR :: Handler TypedContent+getFaviconR = return $ TypedContent "image/x-icon"+ $ toContent $(embedFile "config/favicon.ico")++getRobotsR :: Handler TypedContent+getRobotsR = return $ TypedContent typePlain+ $ toContent $(embedFile "config/robots.txt")+ {-# START_FILE Handler/Home.hs #-} module Handler.Home where import Import-import Yesod.Form.Bootstrap3- ( BootstrapFormLayout (..), renderBootstrap3, withSmallInput )+import Yesod.Form.Bootstrap3 (BootstrapFormLayout (..), renderBootstrap3,+ withSmallInput) -- This is a handler function for the GET request method on the HomeR -- resource pattern. All of your resource patterns are defined in@@ -338,40 +383,20 @@ ( module Import ) where -import Prelude as Import hiding (head, init, last,- readFile, tail, writeFile)-import Yesod as Import hiding (Route (..))--import Control.Applicative as Import (pure, (<$>), (<*>))-import Data.Text as Import (Text)--import Foundation as Import-import Model as Import-import Settings as Import-import Settings.Development as Import-import Settings.StaticFiles as Import--#if __GLASGOW_HASKELL__ >= 704-import Data.Monoid as Import- (Monoid (mappend, mempty, mconcat),- (<>))-#else-import Data.Monoid as Import- (Monoid (mappend, mempty, mconcat))--infixr 5 <>-(<>) :: Monoid m => m -> m -> m-(<>) = mappend-#endif+import ClassyPrelude.Yesod as Import+import Foundation as Import+import Model as Import+import Settings as Import+import Settings.StaticFiles as Import+import Yesod.Auth as Import+import Yesod.Core.Types as Import (loggerSet)+import Yesod.Default.Config2 as Import {-# START_FILE Model.hs #-} module Model where -import Yesod-import Data.Text (Text)+import ClassyPrelude.Yesod import Database.Persist.Quasi-import Data.Typeable (Typeable)-import Prelude -- You can define all of your database entities in the entities file. -- You can find more information on persistent and how to declare entities@@ -401,7 +426,7 @@ Model Settings Settings.StaticFiles- Settings.Development+ Handler.Common Handler.Home if flag(dev) || flag(library-only)@@ -425,17 +450,21 @@ DeriveDataTypeable ViewPatterns TupleSections+ RecordWildCards build-depends: base >= 4 && < 5- , yesod >= 1.4.0 && < 1.5+ , yesod >= 1.4.1 && < 1.5 , yesod-core >= 1.4.0 && < 1.5 , yesod-auth >= 1.4.0 && < 1.5- , yesod-static >= 1.4.0 && < 1.5+ , yesod-static >= 1.4.0.3 && < 1.5 , yesod-form >= 1.4.0 && < 1.5+ , classy-prelude >= 0.10.2+ , classy-prelude-conduit >= 0.10.2+ , classy-prelude-yesod >= 0.10.2 , bytestring >= 0.9 && < 0.11 , text >= 0.11 && < 2.0 , persistent >= 2.0 && < 2.2- , persistent-mysql >= 2.0 && < 2.2+ , persistent-mysql >= 2.1.2 && < 2.2 , persistent-template >= 2.0 && < 2.2 , template-haskell , shakespeare >= 2.0 && < 2.1@@ -452,10 +481,12 @@ , monad-logger >= 0.3 && < 0.4 , fast-logger >= 2.2 && < 2.3 , wai-logger >= 2.2 && < 2.3-- -- see https://github.com/yesodweb/yesod/issues/814- if !os(windows)- build-depends: unix+ , file-embed+ , safe+ , unordered-containers+ , containers+ , vector+ , time executable PROJECTNAME if flag(library-only)@@ -463,9 +494,7 @@ main-is: main.hs hs-source-dirs: app- build-depends: base- , PROJECTNAME- , yesod+ build-depends: base, PROJECTNAME ghc-options: -threaded -O2 -rtsopts -with-rtsopts=-N @@ -493,7 +522,7 @@ build-depends: base , PROJECTNAME- , yesod-test >= 1.4 && < 1.5+ , yesod-test >= 1.4.2 && < 1.5 , yesod-core , yesod , persistent@@ -502,6 +531,8 @@ , monad-logger , transformers , hspec+ , classy-prelude+ , classy-prelude-yesod {-# START_FILE Settings.hs #-} -- | Settings are centralized, as much as possible, into this file. This@@ -511,45 +542,81 @@ -- declared in the Foundation.hs file. module Settings where -import Prelude-import Text.Shakespeare.Text (st)-import Language.Haskell.TH.Syntax-import Database.Persist.MySQL (MySQLConf)-import Yesod.Default.Config-import Yesod.Default.Util-import Data.Text (Text)-import Data.Yaml-import Control.Applicative-import Settings.Development-import Data.Default (def)-import Text.Hamlet+import ClassyPrelude.Yesod+import Control.Exception (throw)+import Data.Aeson (Result (..), fromJSON, withObject, (.!=),+ (.:?))+import Data.FileEmbed (embedFile)+import Data.Yaml (decodeEither')+import Database.Persist.MySQL (MySQLConf)+import Language.Haskell.TH.Syntax (Exp, Name, Q)+import Network.Wai.Handler.Warp (HostPreference)+import Yesod.Default.Config2 (applyEnvValue, configSettingsYml)+import Yesod.Default.Util (WidgetFileSettings, widgetFileNoReload,+ widgetFileReload) --- | Which Persistent backend this site is using.-type PersistConf = MySQLConf+-- | Runtime settings to configure this application. These settings can be+-- loaded from various sources: defaults, environment variables, config files,+-- theoretically even a database.+data AppSettings = AppSettings+ { appStaticDir :: String+ -- ^ Directory from which to serve static files.+ , appDatabaseConf :: MySQLConf+ -- ^ Configuration settings for accessing the database.+ , appRoot :: Text+ -- ^ Base for all generated URLs.+ , appHost :: HostPreference+ -- ^ Host/interface the server should bind to.+ , appPort :: Int+ -- ^ Port to listen on+ , appIpFromHeader :: Bool+ -- ^ Get the IP address from the header when logging. Useful when sitting+ -- behind a reverse proxy. --- Static setting below. Changing these requires a recompile+ , appDetailedRequestLogging :: Bool+ -- ^ Use detailed request logging system+ , appShouldLogAll :: Bool+ -- ^ Should all log messages be displayed?+ , appReloadTemplates :: Bool+ -- ^ Use the reload version of templates+ , appMutableStatic :: Bool+ -- ^ Assume that files in the static dir may change after compilation+ , appSkipCombining :: Bool+ -- ^ Perform no stylesheet/script combining --- | 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-staticDir = "static"+ -- Example app-specific configuration values.+ , appCopyright :: Text+ -- ^ Copyright text to appear in the footer of the page+ , appAnalytics :: Maybe Text+ -- ^ Google Analytics code+ } --- | The base URL for your static files. As you can see by the default--- value, this can simply be "static" appended to your application root.--- A powerful optimization can be serving static files from a separate--- domain name. This allows you to use a web server optimized for static--- files, more easily set expires and cache values, and avoid possibly--- costly transference of cookies on static files. For more information,--- please see:--- http://code.google.com/speed/page-speed/docs/request.html#ServeFromCookielessDomain------ If you change the resource pattern for StaticR in Foundation.hs, you will--- have to make a corresponding change here.------ To see how this value is used, see urlRenderOverride in Foundation.hs-staticRoot :: AppConfig DefaultEnv x -> Text-staticRoot conf = [st|#{appRoot conf}/static|]+instance FromJSON AppSettings where+ parseJSON = withObject "AppSettings" $ \o -> do+ let defaultDev =+#if DEVELOPMENT+ True+#else+ False+#endif+ appStaticDir <- o .: "static-dir"+ appDatabaseConf <- o .: "database"+ appRoot <- o .: "approot"+ appHost <- fromString <$> o .: "host"+ appPort <- o .: "port"+ appIpFromHeader <- o .: "ip-from-header" + appDetailedRequestLogging <- o .:? "detailed-logging" .!= defaultDev+ appShouldLogAll <- o .:? "should-log-all" .!= defaultDev+ appReloadTemplates <- o .:? "reload-templates" .!= defaultDev+ appMutableStatic <- o .:? "mutable-static" .!= defaultDev+ appSkipCombining <- o .:? "skip-combining" .!= defaultDev++ appCopyright <- o .: "copyright"+ appAnalytics <- o .:? "analytics"++ return AppSettings {..}+ -- | Settings for 'widgetFile', such as which template languages to support and -- default Hamlet settings. --@@ -558,81 +625,62 @@ -- https://github.com/yesodweb/yesod/wiki/Overriding-widgetFile widgetFileSettings :: WidgetFileSettings widgetFileSettings = def- { wfsHamletSettings = defaultHamletSettings- { hamletNewlines = AlwaysNewlines- }- } +-- | How static files should be combined.+combineSettings :: CombineSettings+combineSettings = def+ -- The rest of this file contains settings which rarely need changing by a -- user. widgetFile :: String -> Q Exp-widgetFile = (if development then widgetFileReload- else widgetFileNoReload)+widgetFile = (if appReloadTemplates compileTimeAppSettings+ then widgetFileReload+ else widgetFileNoReload) widgetFileSettings -data Extra = Extra- { extraCopyright :: Text- , extraAnalytics :: Maybe Text -- ^ Google Analytics- } deriving Show+-- | Raw bytes at compile time of @config/settings.yml@+configSettingsYmlBS :: ByteString+configSettingsYmlBS = $(embedFile configSettingsYml) -parseExtra :: DefaultEnv -> Object -> Parser Extra-parseExtra _ o = Extra- <$> o .: "copyright"- <*> o .:? "analytics"+-- | @config/settings.yml@, parsed to a @Value@.+configSettingsYmlValue :: Value+configSettingsYmlValue = either throw id $ decodeEither' configSettingsYmlBS -{-# START_FILE Settings/Development.hs #-}-module Settings.Development where+-- | A version of @AppSettings@ parsed at compile time from @config/settings.yml@.+compileTimeAppSettings :: AppSettings+compileTimeAppSettings =+ case fromJSON $ applyEnvValue False mempty configSettingsYmlValue of+ Error e -> error e+ Success settings -> settings -import Prelude+-- The following two functions can be used to combine multiple CSS or JS files+-- at compile time to decrease the number of http requests.+-- Sample usage (inside a Widget):+--+-- > $(combineStylesheets 'StaticR [style1_css, style2_css]) -development :: Bool-development =-#if DEVELOPMENT- True-#else- False-#endif+combineStylesheets :: Name -> [Route Static] -> Q Exp+combineStylesheets = combineStylesheets'+ (appSkipCombining compileTimeAppSettings)+ combineSettings -production :: Bool-production = not development+combineScripts :: Name -> [Route Static] -> Q Exp+combineScripts = combineScripts'+ (appSkipCombining compileTimeAppSettings)+ combineSettings {-# START_FILE Settings/StaticFiles.hs #-} module Settings.StaticFiles where -import Prelude (IO)-import Yesod.Static-import qualified Yesod.Static as Static-import Settings (staticDir)-import Settings.Development-import Language.Haskell.TH (Q, Exp, Name)-import Data.Default (def)---- | use this to create your static file serving site-staticSite :: IO Static.Static-staticSite = if development then Static.staticDevel staticDir- else Static.static staticDir+import Settings (appStaticDir, compileTimeAppSettings)+import Yesod.Static (staticFiles) -- | This generates easy references to files in the static directory at compile time, -- giving you compile-time verification that referenced files exist. -- Warning: any files added to your static directory during run-time can't be -- accessed this way. You'll have to use their FilePath or URL to access them.-$(staticFiles Settings.staticDir)--combineSettings :: CombineSettings-combineSettings = def---- The following two functions can be used to combine multiple CSS or JS files--- at compile time to decrease the number of http requests.--- Sample usage (inside a Widget):------ > $(combineStylesheets 'StaticR [style1_css, style2_css])--combineStylesheets :: Name -> [Route Static] -> Q Exp-combineStylesheets = combineStylesheets' development combineSettings--combineScripts :: Name -> [Route Static] -> Q Exp-combineScripts = combineScripts' development combineSettings+staticFiles (appStaticDir compileTimeAppSettings) {-# START_FILE app/DevelMain.hs #-} -- | Development version to be run inside GHCi.@@ -699,19 +747,24 @@ start :: MVar () -- ^ Written to when the thread is killed. -> IO ThreadId start done = do- (port,app) <- getApplicationDev- forkIO (finally (runSettings (setPort port defaultSettings) app)+ (settings,app) <- getApplicationDev+ forkIO (finally (runSettings settings app) (putMVar done ())) +{-# START_FILE app/devel.hs #-}+{-# LANGUAGE PackageImports #-}+import "PROJECTNAME" Application (develMain)+import Prelude (IO)++main :: IO ()+main = develMain+ {-# START_FILE app/main.hs #-}-import Prelude (IO)-import Yesod.Default.Config (fromArgs)-import Yesod.Default.Main (defaultMainLog)-import Settings (parseExtra)-import Application (makeApplication)+import Prelude (IO)+import Application (appMain) main :: IO ()-main = defaultMainLog (fromArgs parseExtra) makeApplication+main = appMain {-# START_FILE BASE64 config/favicon.ico #-} AAABAAIAEBAAAAEAIABoBAAAJgAAABAQAgABAAEAsAAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAA@@ -816,46 +869,11 @@ -- By default this file is used in Model.hs (which is imported by Foundation.hs) -{-# START_FILE config/mysql.yml #-}-# NOTE: These settings can be overridden by environment variables as well, in-# particular:-#-# MYSQL_HOST-# MYSQL_PORT-# MYSQL_USER-# MYSQL_PASSWORD-# MYSQL_DATABASE--Default: &defaults- user: PROJECTNAME- password: PROJECTNAME- host: localhost- port: 3306- database: PROJECTNAME- poolsize: 10--Development:- <<: *defaults--Testing:- database: PROJECTNAME_test- <<: *defaults--Staging:- database: PROJECTNAME_staging- poolsize: 100- <<: *defaults--Production:- database: PROJECTNAME_production- poolsize: 100- <<: *defaults- {-# START_FILE config/robots.txt #-} User-agent: * {-# START_FILE config/routes #-}-/static StaticR Static getStatic+/static StaticR Static appStatic /auth AuthR Auth getAuth /favicon.ico FaviconR GET@@ -864,155 +882,36 @@ / HomeR GET POST {-# START_FILE config/settings.yml #-}-Default: &defaults- host: "*4" # any IPv4 host- port: 3000- approot: "http://localhost:3000"- copyright: Insert copyright statement here- #analytics: UA-YOURCODE--Development:- <<: *defaults--Testing:- <<: *defaults--Staging:- <<: *defaults--Production:- #approot: "http://www.example.com"- <<: *defaults--{-# START_FILE deploy/Procfile #-}-# Free deployment to Heroku.-#-# !! Warning: You must use a 64 bit machine to compile !!-#-# This could mean using a virtual machine. Give your VM as much memory as you can to speed up linking.-#-# Basic Yesod setup:-#-# * Move this file out of the deploy directory and into your root directory-#-# mv deploy/Procfile ./-#-# * Create an empty package.json-# echo '{ "name": "PROJECTNAME", "version": "0.0.1", "dependencies": {} }' >> package.json-#-# Postgresql Yesod setup:-#-# * add dependencies on the "heroku", "aeson" and "unordered-containers" packages in your cabal file-#-# * add code in Application.hs to use the heroku package and load the connection parameters.-# The below works for Postgresql.-#-# import Data.HashMap.Strict as H-# import Data.Aeson.Types as AT-# #ifndef DEVELOPMENT-# import qualified Web.Heroku-# #endif-#-#-#-# makeFoundation :: AppConfig DefaultEnv Extra -> Logger -> IO App-# makeFoundation conf setLogger = do-# manager <- newManager def-# s <- staticSite-# hconfig <- loadHerokuConfig-# dbconf <- withYamlEnvironment "config/postgresql.yml" (appEnv conf)-# (Database.Persist.Store.loadConfig . combineMappings hconfig) >>=-# Database.Persist.Store.applyEnv-# p <- Database.Persist.Store.createPoolConfig (dbconf :: Settings.PersistConfig)-# Database.Persist.Store.runPool dbconf (runMigration migrateAll) p-# return $ App conf setLogger s p manager dbconf-#-# #ifndef DEVELOPMENT-# canonicalizeKey :: (Text, val) -> (Text, val)-# canonicalizeKey ("dbname", val) = ("database", val)-# canonicalizeKey pair = pair-#-# toMapping :: [(Text, Text)] -> AT.Value-# toMapping xs = AT.Object $ M.fromList $ map (\(key, val) -> (key, AT.String val)) xs-# #endif-#-# combineMappings :: AT.Value -> AT.Value -> AT.Value-# combineMappings (AT.Object m1) (AT.Object m2) = AT.Object $ m1 `M.union` m2-# combineMappings _ _ = error "Data.Object is not a Mapping."-#-# loadHerokuConfig :: IO AT.Value-# loadHerokuConfig = do-# #ifdef DEVELOPMENT-# return $ AT.Object M.empty-# #else-# Web.Heroku.dbConnParams >>= return . toMapping . map canonicalizeKey-# #endif--+static-dir: "_env:STATIC_DIR:static"+host: "_env:HOST:*4" # any IPv4 host+port: "_env:PORT:3000"+approot: "_env:APPROOT:http://localhost:3000"+ip-from-header: "_env:IP_FROM_HEADER:false" -# Heroku setup:-# Find the Heroku guide. Roughly:-#-# * sign up for a heroku account and register your ssh key-# * create a new application on the *cedar* stack-#-# * make your Yesod project the git repository for that application-# * create a deploy branch-#-# git checkout -b deploy-#-# Repeat these steps to deploy:-# * add your web executable binary (referenced below) to the git repository-#-# git checkout deploy-# git add ./dist/build/PROJECTNAME/PROJECTNAME-# git commit -m deploy-#-# * push to Heroku+# Optional values with the following production defaults.+# In development, they default to the inverse. #-# git push heroku deploy:master---# Heroku configuration that runs your app-web: ./dist/build/PROJECTNAME/PROJECTNAME production -p $PORT--{-# START_FILE devel.hs #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE PackageImports #-}-import "PROJECTNAME" Application (getApplicationDev)-import Network.Wai.Handler.Warp- (runSettings, defaultSettings, setPort)-import Control.Concurrent (forkIO)-import System.Directory (doesFileExist, removeFile)-import System.Exit (exitSuccess)-import Control.Concurrent (threadDelay)--#ifndef mingw32_HOST_OS-import System.Posix.Signals (installHandler, sigINT, Handler(Catch))-#endif--main :: IO ()-main = do-#ifndef mingw32_HOST_OS- _ <- installHandler sigINT (Catch $ return ()) Nothing-#endif-- putStrLn "Starting devel application"- (port, app) <- getApplicationDev- forkIO $ runSettings (setPort port defaultSettings) app- loop+# development: false+# detailed-logging: false+# should-log-all: false+# reload-templates: false+# mutable-static: false+# skip-combining: false -loop :: IO ()-loop = do- threadDelay 100000- e <- doesFileExist "yesod-devel/devel-terminate"- if e then terminateDevel else loop+database:+ user: "_env:MYSQL_USER:PROJECTNAME"+ password: "_env:MYSQL_PASSWORD:PROJECTNAME"+ host: "_env:MYSQL_HOST:localhost"+ port: "_env:MYSQL_PORT:5432"+ database: "_env:MYSQL_DATABASE:PROJECTNAME"+ poolsize: "_env:MYSQL_POOLSIZE:10" -terminateDevel :: IO ()-terminateDevel = exitSuccess+copyright: Insert copyright statement here+#analytics: UA-YOURCODE -{-# START_FILE messages/en.msg #-}-Hello: Hello+{-# START_FILE config/test-settings.yml #-}+database:+ database: PROJECTNAME_test {-# START_FILE static/css/bootstrap.css #-} /*!@@ -8978,9 +8877,9 @@ <div id="main" role="main"> ^{pageBody pc} <footer>- #{extraCopyright $ appExtra $ settings master}+ #{appCopyright $ appSettings master} - $maybe analytics <- extraAnalytics $ appExtra $ settings master+ $maybe analytics <- appAnalytics $ appSettings master <script> if(!window.location.href.match(/localhost/)){ window._gaq = [['_setAccount','#{analytics}'],['_trackPageview'],['_trackPageLoadTime']];@@ -9003,7 +8902,7 @@ ^{widget} {-# START_FILE templates/homepage.hamlet #-}-<h1>_{MsgHello}+<h1>Welcome to Yesod! <ol> <li>Now that you have a working project you should use the #@@ -9054,92 +8953,88 @@ color: #990 } -{-# START_FILE test/Handler/HomeSpec.hs #-}-module Handler.HomeSpec- ( spec- ) where+{-# START_FILE test/Handler/CommonSpec.hs #-}+module Handler.CommonSpec (spec) where import TestImport-import qualified Data.List as L spec :: Spec-spec =- ydescribe "These are some example tests" $ do-- yit "loads the index and checks it looks right" $ do- get HomeR+spec = withApp $ do+ describe "robots.txt" $ do+ it "gives a 200" $ do+ get RobotsR statusIs 200- htmlAllContain "h1" "Hello"+ it "has correct User-agent" $ do+ get RobotsR+ bodyContains "User-agent: *"+ describe "favicon.ico" $ do+ it "gives a 200" $ do+ get FaviconR+ statusIs 200 - request $ do- setMethod "POST"- setUrl HomeR- addNonce- fileByLabel "Choose a file" "test/Spec.hs" "text/plain" -- talk about self-reference- byLabel "What's on the file?" "Some Content"+{-# START_FILE test/Handler/HomeSpec.hs #-}+module Handler.HomeSpec (spec) where - statusIs 200- -- more debugging printBody- htmlCount ".message" 1- htmlAllContain ".message" "Some Content"- htmlAllContain ".message" "text/plain"+import TestImport - -- This is a simple example of using a database access in a test. The- -- test will succeed for a fresh scaffolded site with an empty database,- -- but will fail on an existing database with a non-empty user table.- yit "leaves the user table empty" $ do- get HomeR- statusIs 200- users <- runDB $ selectList ([] :: [Filter User]) []- assertEqual "user table empty" 0 $ L.length users+spec :: Spec+spec = withApp $ do+ it "loads the index and checks it looks right" $ do+ get HomeR+ statusIs 200+ htmlAllContain "h1" "Welcome to Yesod" -{-# START_FILE test/Spec.hs #-}-module Main where+ request $ do+ setMethod "POST"+ setUrl HomeR+ addNonce+ fileByLabel "Choose a file" "test/Spec.hs" "text/plain" -- talk about self-reference+ byLabel "What's on the file?" "Some Content" -import Import-import Yesod.Default.Config-import Yesod.Test-import Test.Hspec (hspec)-import Application (makeFoundation)+ statusIs 200+ -- more debugging printBody+ htmlCount ".message" 1+ htmlAllContain ".message" "Some Content"+ htmlAllContain ".message" "text/plain" -import qualified Handler.HomeSpec+ -- This is a simple example of using a database access in a test. The+ -- test will succeed for a fresh scaffolded site with an empty database,+ -- but will fail on an existing database with a non-empty user table.+ it "leaves the user table empty" $ do+ get HomeR+ statusIs 200+ users <- runDB $ selectList ([] :: [Filter User]) []+ assertEqual "user table empty" 0 $ length users -main :: IO ()-main = do- conf <- Yesod.Default.Config.loadConfig $ (configSettings Testing)- { csParseExtra = parseExtra- }- foundation <- makeFoundation conf- hspec $ do- yesodSpec foundation $ do- Handler.HomeSpec.spec+{-# START_FILE test/Spec.hs #-}+{-# OPTIONS_GHC -F -pgmF hspec-discover #-} {-# START_FILE test/TestImport.hs #-} module TestImport- ( module Yesod.Test- , module Model- , module Foundation- , module Database.Persist- , module Prelude- , runDB- , Spec- , Example+ ( module TestImport+ , module X ) where -import Yesod.Test-import Database.Persist hiding (get)-import Database.Persist.Sql (SqlPersistM, runSqlPersistMPool)-import Control.Monad.IO.Class (liftIO)-import Prelude--import Foundation-import Model--type Spec = YesodSpec App-type Example = YesodExample App+import Application (makeFoundation)+import ClassyPrelude as X+import Database.Persist as X hiding (get)+import Database.Persist.Sql (SqlPersistM, runSqlPersistMPool)+import Foundation as X+import Model as X+import Test.Hspec as X+import Yesod.Default.Config2 (ignoreEnv, loadAppSettings)+import Yesod.Test as X -runDB :: SqlPersistM a -> Example a+runDB :: SqlPersistM a -> YesodExample App a runDB query = do- pool <- fmap connPool getTestYesod+ pool <- fmap appConnPool getTestYesod liftIO $ runSqlPersistMPool query pool++withApp :: SpecWith App -> Spec+withApp = before $ do+ settings <- loadAppSettings+ ["config/test-settings.yml", "config/settings.yml"]+ []+ ignoreEnv+ makeFoundation settings
hsfiles/postgres-fay.hsfiles view
@@ -31,33 +31,32 @@ {-# START_FILE Application.hs #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Application- ( makeApplication- , getApplicationDev+ ( getApplicationDev+ , appMain+ , develMain , makeFoundation ) where +import Control.Monad.Logger (liftLoc, runLoggingT)+import Database.Persist.Postgresql (createPostgresqlPool, pgConnStr,+ pgPoolSize, runSqlPool) import Import-import Yesod.Auth-import Yesod.Default.Config-import Yesod.Default.Main-import Yesod.Default.Handlers-import Network.Wai.Middleware.RequestLogger- ( mkRequestLogger, outputFormat, OutputFormat (..), IPAddrSource (..), destination- )-import qualified Network.Wai.Middleware.RequestLogger as RequestLogger-import qualified Database.Persist-import Database.Persist.Sql (runMigration)-import Database.Persist.Postgresql (createPostgresqlPool, pgConnStr, pgPoolSize)-import Network.HTTP.Client.Conduit (newManager)-import Yesod.Fay (getFaySite)-import Control.Monad.Logger (runLoggingT)-import System.Log.FastLogger (newStdoutLoggerSet, defaultBufSize)-import Network.Wai.Logger (clockDateCacher)-import Data.Default (def)-import Yesod.Core.Types (loggerSet, Logger (Logger))+import Language.Haskell.TH.Syntax (qLocation)+import Network.Wai.Handler.Warp (Settings, defaultSettings,+ defaultShouldDisplayException,+ runSettings, setHost,+ setOnException, setPort)+import Network.Wai.Middleware.RequestLogger (Destination (Logger),+ IPAddrSource (..),+ OutputFormat (..), destination,+ mkRequestLogger, outputFormat)+import System.Log.FastLogger (defaultBufSize, newStdoutLoggerSet,+ toLogStr)+import Yesod.Fay (getFaySite) -- Import all relevant handler modules here. -- Don't forget to add new modules to your cabal file!+import Handler.Common import Handler.Fay import Handler.Home @@ -66,114 +65,138 @@ -- comments there for more details. mkYesodDispatch "App" resourcesApp --- This function allocates resources (such as a database connection pool),--- performs initialization and creates a WAI application. This is also the--- place to put your migrate statements to have automatic database+-- | This function allocates resources (such as a database connection pool),+-- performs initialization and return a foundation datatype value. This is also+-- the place to put your migrate statements to have automatic database -- migrations handled by Yesod.-makeApplication :: AppConfig DefaultEnv Extra -> IO (Application, LogFunc)-makeApplication conf = do- foundation <- makeFoundation conf+makeFoundation :: AppSettings -> IO App+makeFoundation appSettings = do+ -- Some basic initializations: HTTP connection manager, logger, and static+ -- subsite.+ appHttpManager <- newManager+ appLogger <- newStdoutLoggerSet defaultBufSize >>= makeYesodLogger+ appStatic <-+ (if appMutableStatic appSettings then staticDevel else static)+ (appStaticDir appSettings)+ let appFayCommandHandler = onCommand - -- Initialize the logging middleware+ -- We need a log function to create a connection pool. We need a connection+ -- pool to create our foundation. And we need our foundation to get a+ -- logging function. To get out of this loop, we initially create a+ -- temporary foundation without a real connection pool, get a log function+ -- from there, and then create the real foundation.+ let mkFoundation appConnPool = App {..}+ tempFoundation = mkFoundation $ error "connPool forced in tempFoundation"+ logFunc = messageLoggerSource tempFoundation appLogger++ -- Create the database connection pool+ pool <- flip runLoggingT logFunc $ createPostgresqlPool+ (pgConnStr $ appDatabaseConf appSettings)+ (pgPoolSize $ appDatabaseConf appSettings)++ -- Perform database migration using our application's logging settings.+ runLoggingT (runSqlPool (runMigration migrateAll) pool) logFunc++ -- Return the foundation+ return $ mkFoundation pool++-- | Convert our foundation to a WAI Application by calling @toWaiAppPlain@ and+-- applyng some additional middlewares.+makeApplication :: App -> IO Application+makeApplication foundation = do logWare <- mkRequestLogger def { outputFormat =- if development+ if appDetailedRequestLogging $ appSettings foundation then Detailed True- else Apache FromSocket- , destination = RequestLogger.Logger $ loggerSet $ appLogger foundation+ else Apache+ (if appIpFromHeader $ appSettings foundation+ then FromFallback+ else FromSocket)+ , destination = Logger $ loggerSet $ appLogger foundation } -- Create the WAI application and apply middlewares- app <- toWaiAppPlain foundation- let logFunc = messageLoggerSource foundation (appLogger foundation)- return (logWare $ defaultMiddlewaresNoLogging app, logFunc)+ appPlain <- toWaiAppPlain foundation+ return $ logWare $ defaultMiddlewaresNoLogging appPlain --- | Loads up any necessary settings, creates your foundation datatype, and--- performs some initialization.-makeFoundation :: AppConfig DefaultEnv Extra -> IO App-makeFoundation conf = do- manager <- newManager- s <- staticSite- dbconf <- withYamlEnvironment "config/postgresql.yml" (appEnv conf)- Database.Persist.loadConfig >>=- Database.Persist.applyEnv+-- | Warp settings for the given foundation value.+warpSettings :: App -> Settings+warpSettings foundation =+ setPort (appPort $ appSettings foundation)+ $ setHost (appHost $ appSettings foundation)+ $ setOnException (\_req e ->+ when (defaultShouldDisplayException e) $ messageLoggerSource+ foundation+ (appLogger foundation)+ $(qLocation >>= liftLoc)+ "yesod"+ LevelError+ (toLogStr $ "Exception from Warp: " ++ show e))+ defaultSettings - loggerSet' <- newStdoutLoggerSet defaultBufSize- (getter, _) <- clockDateCacher+-- | For yesod devel, return the Warp settings and WAI Application.+getApplicationDev :: IO (Settings, Application)+getApplicationDev = do+ settings <- loadAppSettings [configSettingsYml] [] useEnv+ foundation <- makeFoundation settings+ app <- makeApplication foundation+ wsettings <- getDevSettings $ warpSettings foundation+ return (wsettings, app) - let logger = Yesod.Core.Types.Logger loggerSet' getter- mkFoundation p = App- { settings = conf- , getStatic = s- , connPool = p- , httpManager = manager- , persistConfig = dbconf- , fayCommandHandler = onCommand- , appLogger = logger- }- tempFoundation = mkFoundation $ error "connPool forced in tempFoundation"- logFunc = messageLoggerSource tempFoundation logger+-- | main function for use by yesod devel+develMain :: IO ()+develMain = develMainHelper getApplicationDev - p <- flip runLoggingT logFunc- $ createPostgresqlPool (pgConnStr dbconf) (pgPoolSize dbconf)- let foundation = mkFoundation p+-- | The @main@ function for an executable running this site.+appMain :: IO ()+appMain = do+ -- Get the settings from all relevant sources+ settings <- loadAppSettingsArgs+ -- fall back to compile-time values, set to [] to require values at runtime+ [configSettingsYmlValue] - -- Perform database migration using our application's logging settings.- flip runLoggingT logFunc- (Database.Persist.runPool dbconf (runMigration migrateAll) p)+ -- allow environment variables to override+ useEnv - return foundation+ -- Generate the foundation from the settings+ foundation <- makeFoundation settings --- for yesod devel-getApplicationDev :: IO (Int, Application)-getApplicationDev =- defaultDevelApp loader (fmap fst . makeApplication)- where- loader = Yesod.Default.Config.loadConfig (configSettings Development)- { csParseExtra = parseExtra- }+ -- Generate a WAI Application from the foundation+ app <- makeApplication foundation + -- Run the application with Warp+ runSettings (warpSettings foundation) app+ {-# START_FILE Foundation.hs #-} module Foundation where -import Prelude-import Yesod-import Yesod.Static-import Yesod.Auth-import Yesod.Auth.BrowserId-import Yesod.Default.Config-import Yesod.Default.Util (addStaticContentExternal)-import Network.HTTP.Client.Conduit (Manager, HasHttpManager (getHttpManager))-import qualified Settings-import Settings.Development (development)-import qualified Database.Persist-import Database.Persist.Sql (SqlBackend)-import Settings.StaticFiles-import Settings (widgetFile, Extra (..))+import ClassyPrelude.Yesod+import Database.Persist.Sql (ConnectionPool, runSqlPool) import Model-import Text.Hamlet (hamletFile)+import Settings+import Settings.StaticFiles+import Text.Hamlet (hamletFile)+import Yesod.Auth+import Yesod.Auth.BrowserId (authBrowserId)+import Yesod.Core.Types (Logger)+import Yesod.Default.Util (addStaticContentExternal) import Yesod.Fay-import Yesod.Core.Types (Logger) --- | The site argument for your application. This can be a good place to+-- | The foundation datatype for your application. This can be a good place to -- keep settings and values requiring initialization before your application -- starts running, such as database connections. Every handler will have -- access to the data present here. data App = App- { settings :: AppConfig DefaultEnv Extra- , getStatic :: Static -- ^ Settings for static file serving.- , connPool :: Database.Persist.PersistConfigPool Settings.PersistConf -- ^ Database connection pool.- , httpManager :: Manager- , persistConfig :: Settings.PersistConf- , fayCommandHandler :: CommandHandler App- , appLogger :: Logger+ { appSettings :: AppSettings+ , appStatic :: Static -- ^ Settings for static file serving.+ , appConnPool :: ConnectionPool -- ^ Database connection pool.+ , appHttpManager :: Manager+ , appLogger :: Logger+ , appFayCommandHandler :: CommandHandler App } instance HasHttpManager App where- getHttpManager = httpManager---- Set up i18n messages. See the message folder.-mkMessage "App" "messages" "en"+ getHttpManager = appHttpManager -- This is where we define all of the routes in our application. For a full -- explanation of the syntax, please see:@@ -184,12 +207,15 @@ -- explanation for this split. mkYesodData "App" $(parseRoutesFile "config/routes") +-- | A convenient synonym for creating forms. type Form x = Html -> MForm (HandlerT App IO) (FormResult x, Widget) -- Please see the documentation for the Yesod typeclass. There are a number -- of settings which can be configured by overriding methods here. instance Yesod App where- approot = ApprootMaster $ appRoot . settings+ -- Controls the base of generated URLs. For more information on modifying,+ -- see: https://github.com/yesodweb/yesod/wiki/Overriding-approot+ approot = ApprootMaster $ appRoot . appSettings -- Store session data on the client in encrypted cookies, -- default session idle timeout is 120 minutes@@ -212,12 +238,6 @@ $(widgetFile "default-layout") withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet") - -- This is done to provide an optimization for serving static files from- -- a separate domain. Please see the staticRoot setting in Settings.hs- urlRenderOverride y (StaticR s) =- Just $ uncurry (joinPath y (Settings.staticRoot $ settings y)) $ renderRoute s- urlRenderOverride _ _ = Nothing- -- The page to be redirected to when authentication is required. authRoute _ = Just $ AuthR LoginR @@ -232,21 +252,27 @@ -- 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 =- addStaticContentExternal Right genFileName Settings.staticDir (StaticR . flip StaticRoute [])+ addStaticContent ext mime content = do+ master <- getYesod+ let staticDir = appStaticDir $ appSettings master+ addStaticContentExternal+ Right+ genFileName+ staticDir+ (StaticR . flip StaticRoute [])+ ext+ mime+ content where -- Generate a unique filename based on the content itself- genFileName lbs- | development = "autogen-" ++ base64md5 lbs- | otherwise = base64md5 lbs-- -- Place Javascript at bottom of the body tag so the rest of the page loads first- jsLoader _ = BottomOfBody+ genFileName lbs = "autogen-" ++ base64md5 lbs -- What messages should be logged. The following includes all messages when -- in development, and warnings and errors in production.- shouldLog _ _source level =- development || level == LevelWarn || level == LevelError+ shouldLog app _source level =+ appShouldLogAll (appSettings app)+ || level == LevelWarn+ || level == LevelError makeLogger = return . appLogger @@ -257,14 +283,16 @@ yesodFayCommand render command = do master <- getYesod- fayCommandHandler master render command+ appFayCommandHandler master render command -- How to run database actions. instance YesodPersist App where type YesodPersistBackend App = SqlBackend- runDB = defaultRunDB persistConfig connPool+ runDB action = do+ master <- getYesod+ runSqlPool action $ appConnPool master instance YesodPersistRunner App where- getDBRunner = defaultGetDBRunner connPool+ getDBRunner = defaultGetDBRunner appConnPool instance YesodAuth App where type AuthId App = UserId@@ -273,6 +301,8 @@ loginDest _ = HomeR -- Where to send a user after logout logoutDest _ = HomeR+ -- Override the above two destinations when a Referer: header is present+ redirectToReferer _ = True getAuthId creds = runDB $ do x <- getBy $ UniqueUser $ credsIdent creds@@ -287,7 +317,7 @@ -- You can add other plugins like BrowserID, email or OAuth here authPlugins _ = [authBrowserId def] - authHttpManager = httpManager+ authHttpManager = getHttpManager instance YesodAuthPersist App @@ -296,23 +326,39 @@ instance RenderMessage App FormMessage where renderMessage _ _ = defaultFormMessage --- | Get the 'Extra' value, used to hold data from the settings.yml file.-getExtra :: Handler Extra-getExtra = fmap (appExtra . settings) getYesod---- Note: previous versions of the scaffolding included a deliver function to--- send emails. Unfortunately, there are too many different options for us to--- give a reasonable default. Instead, the information is available on the--- wiki:+-- Note: Some functionality previously present in the scaffolding has been+-- moved to documentation in the Wiki. Following are some hopefully helpful+-- links: -- -- https://github.com/yesodweb/yesod/wiki/Sending-email+-- https://github.com/yesodweb/yesod/wiki/Serve-static-files-from-a-separate-domain+-- https://github.com/yesodweb/yesod/wiki/i18n-messages-in-the-scaffolding +{-# START_FILE Handler/Common.hs #-}+-- | Common handler functions.+module Handler.Common where++import Data.FileEmbed (embedFile)+import Import++-- These handlers embed files in the executable at compile time to avoid a+-- runtime dependency, and for efficiency.++getFaviconR :: Handler TypedContent+getFaviconR = return $ TypedContent "image/x-icon"+ $ toContent $(embedFile "config/favicon.ico")++getRobotsR :: Handler TypedContent+getRobotsR = return $ TypedContent typePlain+ $ toContent $(embedFile "config/robots.txt")+ {-# START_FILE Handler/Fay.hs #-} module Handler.Fay where +import Fay.Convert (readFromFay) import Import+import Prelude ((!!)) import Yesod.Fay-import Fay.Convert (readFromFay) fibs :: [Int] fibs = 0 : 1 : zipWith (+) fibs (drop 1 fibs)@@ -327,9 +373,8 @@ module Handler.Home where import Import-import Language.Haskell.TH ( Exp(..) )-import Yesod.Form.Bootstrap3- ( BootstrapFormLayout (..), renderBootstrap3, withSmallInput )+import Yesod.Form.Bootstrap3 (BootstrapFormLayout (..), renderBootstrap3,+ withSmallInput) -- This is a handler function for the GET request method on the HomeR -- resource pattern. All of your resource patterns are defined in@@ -347,7 +392,7 @@ aDomId <- newIdent setTitle "Welcome To Yesod!" $(widgetFile "homepage")- $(fayFile' (ConE 'StaticR) "Home")+ $(fayFile "Home") postHomeR :: Handler Html postHomeR = do@@ -372,41 +417,26 @@ ( module Import ) where -import Prelude as Import hiding (head, init, last,- readFile, tail, writeFile)-import Yesod as Import hiding (Route (..))--import Control.Applicative as Import (pure, (<$>), (<*>))-import Data.Text as Import (Text)--import Foundation as Import-import Model as Import-import Settings as Import-import Settings.Development as Import-import Settings.StaticFiles as Import-import SharedTypes as Import--#if __GLASGOW_HASKELL__ >= 704-import Data.Monoid as Import- (Monoid (mappend, mempty, mconcat),- (<>))-#else-import Data.Monoid as Import- (Monoid (mappend, mempty, mconcat))+import ClassyPrelude.Yesod as Import+import Foundation as Import+import Language.Haskell.TH.Syntax (Exp (ConE))+import Model as Import+import Settings as Import+import Settings.StaticFiles as Import+import SharedTypes as Import+import Yesod.Auth as Import+import Yesod.Core.Types as Import (loggerSet)+import Yesod.Default.Config2 as Import+import Yesod.Fay (FayFile) -infixr 5 <>-(<>) :: Monoid m => m -> m -> m-(<>) = mappend-#endif+fayFile :: FayFile+fayFile = fayFile' (ConE 'StaticR) {-# START_FILE Model.hs #-} module Model where -import Yesod-import Data.Text (Text)+import ClassyPrelude.Yesod import Database.Persist.Quasi-import Data.Typeable (Typeable)-import Prelude -- You can define all of your database entities in the entities file. -- You can find more information on persistent and how to declare entities@@ -437,8 +467,8 @@ Model Settings Settings.StaticFiles- Settings.Development SharedTypes+ Handler.Common Handler.Fay Handler.Home @@ -464,19 +494,23 @@ DeriveDataTypeable ViewPatterns TupleSections+ RecordWildCards build-depends: base >= 4 && < 5- , yesod >= 1.4.0 && < 1.5+ , yesod >= 1.4.1 && < 1.5 , yesod-core >= 1.4.0 && < 1.5 , yesod-auth >= 1.4.0 && < 1.5- , yesod-static >= 1.4.0 && < 1.5+ , yesod-static >= 1.4.0.3 && < 1.5 , yesod-form >= 1.4.0 && < 1.5 , yesod-fay >= 0.7 && < 0.8 , fay >= 0.21.2.1 && < 0.22+ , classy-prelude >= 0.10.2+ , classy-prelude-conduit >= 0.10.2+ , classy-prelude-yesod >= 0.10.2 , bytestring >= 0.9 && < 0.11 , text >= 0.11 && < 2.0 , persistent >= 2.0 && < 2.2- , persistent-postgresql >= 2.0 && < 2.2+ , persistent-postgresql >= 2.1.1 && < 2.2 , persistent-template >= 2.0 && < 2.2 , template-haskell , shakespeare >= 2.0 && < 2.1@@ -492,10 +526,12 @@ , monad-logger >= 0.3 && < 0.4 , fast-logger >= 2.2 && < 2.3 , wai-logger >= 2.2 && < 2.3-- -- see https://github.com/yesodweb/yesod/issues/814- if !os(windows)- build-depends: unix+ , file-embed+ , safe+ , unordered-containers+ , containers+ , vector+ , time executable PROJECTNAME if flag(library-only)@@ -503,9 +539,7 @@ main-is: main.hs hs-source-dirs: app- build-depends: base- , PROJECTNAME- , yesod+ build-depends: base, PROJECTNAME ghc-options: -threaded -O2 -rtsopts -with-rtsopts=-N @@ -533,7 +567,7 @@ build-depends: base , PROJECTNAME- , yesod-test >= 1.4 && < 1.5+ , yesod-test >= 1.4.2 && < 1.5 , yesod-core , yesod , persistent@@ -542,6 +576,8 @@ , monad-logger , transformers , hspec+ , classy-prelude+ , classy-prelude-yesod {-# START_FILE Settings.hs #-} -- | Settings are centralized, as much as possible, into this file. This@@ -551,46 +587,82 @@ -- declared in the Foundation.hs file. module Settings where -import Prelude-import Text.Shakespeare.Text (st)-import Language.Haskell.TH.Syntax+import ClassyPrelude.Yesod+import Control.Exception (throw)+import Data.Aeson (Result (..), fromJSON, withObject, (.!=),+ (.:?))+import Data.FileEmbed (embedFile)+import Data.Yaml (decodeEither') import Database.Persist.Postgresql (PostgresConf)-import Yesod.Default.Config-import Yesod.Default.Util-import Data.Text (Text)-import Data.Yaml-import Control.Applicative-import Settings.Development-import Data.Default (def)-import Text.Hamlet+import Language.Haskell.TH.Syntax (Exp, Name, Q)+import Network.Wai.Handler.Warp (HostPreference)+import Yesod.Default.Config2 (applyEnvValue, configSettingsYml)+import Yesod.Default.Util (WidgetFileSettings, widgetFileNoReload,+ widgetFileReload) import Yesod.Fay --- | Which Persistent backend this site is using.-type PersistConf = PostgresConf+-- | Runtime settings to configure this application. These settings can be+-- loaded from various sources: defaults, environment variables, config files,+-- theoretically even a database.+data AppSettings = AppSettings+ { appStaticDir :: String+ -- ^ Directory from which to serve static files.+ , appDatabaseConf :: PostgresConf+ -- ^ Configuration settings for accessing the database.+ , appRoot :: Text+ -- ^ Base for all generated URLs.+ , appHost :: HostPreference+ -- ^ Host/interface the server should bind to.+ , appPort :: Int+ -- ^ Port to listen on+ , appIpFromHeader :: Bool+ -- ^ Get the IP address from the header when logging. Useful when sitting+ -- behind a reverse proxy. --- Static setting below. Changing these requires a recompile+ , appDetailedRequestLogging :: Bool+ -- ^ Use detailed request logging system+ , appShouldLogAll :: Bool+ -- ^ Should all log messages be displayed?+ , appReloadTemplates :: Bool+ -- ^ Use the reload version of templates+ , appMutableStatic :: Bool+ -- ^ Assume that files in the static dir may change after compilation+ , appSkipCombining :: Bool+ -- ^ Perform no stylesheet/script combining --- | 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-staticDir = "static"+ -- Example app-specific configuration values.+ , appCopyright :: Text+ -- ^ Copyright text to appear in the footer of the page+ , appAnalytics :: Maybe Text+ -- ^ Google Analytics code+ } --- | The base URL for your static files. As you can see by the default--- value, this can simply be "static" appended to your application root.--- A powerful optimization can be serving static files from a separate--- domain name. This allows you to use a web server optimized for static--- files, more easily set expires and cache values, and avoid possibly--- costly transference of cookies on static files. For more information,--- please see:--- http://code.google.com/speed/page-speed/docs/request.html#ServeFromCookielessDomain------ If you change the resource pattern for StaticR in Foundation.hs, you will--- have to make a corresponding change here.------ To see how this value is used, see urlRenderOverride in Foundation.hs-staticRoot :: AppConfig DefaultEnv x -> Text-staticRoot conf = [st|#{appRoot conf}/static|]+instance FromJSON AppSettings where+ parseJSON = withObject "AppSettings" $ \o -> do+ let defaultDev =+#if DEVELOPMENT+ True+#else+ False+#endif+ appStaticDir <- o .: "static-dir"+ appDatabaseConf <- o .: "database"+ appRoot <- o .: "approot"+ appHost <- fromString <$> o .: "host"+ appPort <- o .: "port"+ appIpFromHeader <- o .: "ip-from-header" + appDetailedRequestLogging <- o .:? "detailed-logging" .!= defaultDev+ appShouldLogAll <- o .:? "should-log-all" .!= defaultDev+ appReloadTemplates <- o .:? "reload-templates" .!= defaultDev+ appMutableStatic <- o .:? "mutable-static" .!= defaultDev+ appSkipCombining <- o .:? "skip-combining" .!= defaultDev++ appCopyright <- o .: "copyright"+ appAnalytics <- o .:? "analytics"++ return AppSettings {..}+ -- | Settings for 'widgetFile', such as which template languages to support and -- default Hamlet settings. --@@ -599,23 +671,26 @@ -- https://github.com/yesodweb/yesod/wiki/Overriding-widgetFile widgetFileSettings :: WidgetFileSettings widgetFileSettings = def- { wfsHamletSettings = defaultHamletSettings- { hamletNewlines = AlwaysNewlines- }- } +-- | How static files should be combined.+combineSettings :: CombineSettings+combineSettings = def+ -- The rest of this file contains settings which rarely need changing by a -- user. widgetFile :: String -> Q Exp-widgetFile = (if development then widgetFileReload- else widgetFileNoReload)+widgetFile = (if appReloadTemplates compileTimeAppSettings+ then widgetFileReload+ else widgetFileNoReload) widgetFileSettings fayFile' :: Exp -> FayFile-fayFile' staticR moduleName- | development = fayFileReload settings- | otherwise = fayFileProd settings+fayFile' staticR moduleName =+ (if appReloadTemplates compileTimeAppSettings+ then fayFileReload+ else fayFileProd)+ settings where settings = (yesodFaySettings moduleName) { yfsSeparateRuntime = Just ("static", staticR)@@ -624,68 +699,48 @@ , yfsPackages = ["fay-dom"] } -data Extra = Extra- { extraCopyright :: Text- , extraAnalytics :: Maybe Text -- ^ Google Analytics- } deriving Show+-- | Raw bytes at compile time of @config/settings.yml@+configSettingsYmlBS :: ByteString+configSettingsYmlBS = $(embedFile configSettingsYml) -parseExtra :: DefaultEnv -> Object -> Parser Extra-parseExtra _ o = Extra- <$> o .: "copyright"- <*> o .:? "analytics"+-- | @config/settings.yml@, parsed to a @Value@.+configSettingsYmlValue :: Value+configSettingsYmlValue = either throw id $ decodeEither' configSettingsYmlBS -{-# START_FILE Settings/Development.hs #-}-module Settings.Development where+-- | A version of @AppSettings@ parsed at compile time from @config/settings.yml@.+compileTimeAppSettings :: AppSettings+compileTimeAppSettings =+ case fromJSON $ applyEnvValue False mempty configSettingsYmlValue of+ Error e -> error e+ Success settings -> settings -import Prelude+-- The following two functions can be used to combine multiple CSS or JS files+-- at compile time to decrease the number of http requests.+-- Sample usage (inside a Widget):+--+-- > $(combineStylesheets 'StaticR [style1_css, style2_css]) -development :: Bool-development =-#if DEVELOPMENT- True-#else- False-#endif+combineStylesheets :: Name -> [Route Static] -> Q Exp+combineStylesheets = combineStylesheets'+ (appSkipCombining compileTimeAppSettings)+ combineSettings -production :: Bool-production = not development+combineScripts :: Name -> [Route Static] -> Q Exp+combineScripts = combineScripts'+ (appSkipCombining compileTimeAppSettings)+ combineSettings {-# START_FILE Settings/StaticFiles.hs #-} module Settings.StaticFiles where -import Prelude (IO)-import Yesod.Static-import qualified Yesod.Static as Static-import Settings (staticDir)-import Settings.Development-import Language.Haskell.TH (Q, Exp, Name)-import Data.Default (def)---- | use this to create your static file serving site-staticSite :: IO Static.Static-staticSite = if development then Static.staticDevel staticDir- else Static.static staticDir+import Settings (appStaticDir, compileTimeAppSettings)+import Yesod.Static (staticFiles) -- | This generates easy references to files in the static directory at compile time, -- giving you compile-time verification that referenced files exist. -- Warning: any files added to your static directory during run-time can't be -- accessed this way. You'll have to use their FilePath or URL to access them.-$(staticFiles Settings.staticDir)--combineSettings :: CombineSettings-combineSettings = def---- The following two functions can be used to combine multiple CSS or JS files--- at compile time to decrease the number of http requests.--- Sample usage (inside a Widget):------ > $(combineStylesheets 'StaticR [style1_css, style2_css])--combineStylesheets :: Name -> [Route Static] -> Q Exp-combineStylesheets = combineStylesheets' development combineSettings--combineScripts :: Name -> [Route Static] -> Q Exp-combineScripts = combineScripts' development combineSettings+staticFiles (appStaticDir compileTimeAppSettings) {-# START_FILE app/DevelMain.hs #-} -- | Development version to be run inside GHCi.@@ -752,19 +807,24 @@ start :: MVar () -- ^ Written to when the thread is killed. -> IO ThreadId start done = do- (port,app) <- getApplicationDev- forkIO (finally (runSettings (setPort port defaultSettings) app)+ (settings,app) <- getApplicationDev+ forkIO (finally (runSettings settings app) (putMVar done ())) +{-# START_FILE app/devel.hs #-}+{-# LANGUAGE PackageImports #-}+import "PROJECTNAME" Application (develMain)+import Prelude (IO)++main :: IO ()+main = develMain+ {-# START_FILE app/main.hs #-}-import Prelude (IO)-import Yesod.Default.Config (fromArgs)-import Yesod.Default.Main (defaultMainLog)-import Settings (parseExtra)-import Application (makeApplication)+import Prelude (IO)+import Application (appMain) main :: IO ()-main = defaultMainLog (fromArgs parseExtra) makeApplication+main = appMain {-# START_FILE BASE64 config/favicon.ico #-} AAABAAIAEBAAAAEAIABoBAAAJgAAABAQAgABAAEAsAAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAA@@ -869,46 +929,11 @@ -- By default this file is used in Model.hs (which is imported by Foundation.hs) -{-# START_FILE config/postgresql.yml #-}-# NOTE: These settings can be overridden by environment variables as well, in-# particular:-#-# PGHOST-# PGPORT-# PGUSER-# PGPASS-# PGDATABASE--Default: &defaults- user: PROJECTNAME- password: PROJECTNAME- host: localhost- port: 5432- database: PROJECTNAME- poolsize: 10--Development:- <<: *defaults--Testing:- database: PROJECTNAME_test- <<: *defaults--Staging:- database: PROJECTNAME_staging- poolsize: 100- <<: *defaults--Production:- database: PROJECTNAME_production- poolsize: 100- <<: *defaults- {-# START_FILE config/robots.txt #-} User-agent: * {-# START_FILE config/routes #-}-/static StaticR Static getStatic+/static StaticR Static appStatic /auth AuthR Auth getAuth /fay-command FaySiteR FaySite getFaySite @@ -918,152 +943,36 @@ / HomeR GET POST {-# START_FILE config/settings.yml #-}-Default: &defaults- host: "*4" # any IPv4 host- port: 3000- approot: "http://localhost:3000"- copyright: Insert copyright statement here- #analytics: UA-YOURCODE--Development:- <<: *defaults--Testing:- <<: *defaults--Staging:- <<: *defaults--Production:- #approot: "http://www.example.com"- <<: *defaults--{-# START_FILE deploy/Procfile #-}-# Free deployment to Heroku.-#-# !! Warning: You must use a 64 bit machine to compile !!-#-# This could mean using a virtual machine. Give your VM as much memory as you can to speed up linking.-#-# Basic Yesod setup:-#-# * Move this file out of the deploy directory and into your root directory-#-# mv deploy/Procfile ./-#-# * Create an empty package.json-# echo '{ "name": "PROJECTNAME", "version": "0.0.1", "dependencies": {} }' >> package.json-#-# Postgresql Yesod setup:-#-# * add dependencies on the "heroku", "aeson" and "unordered-containers" packages in your cabal file-#-# * add code in Application.hs to use the heroku package and load the connection parameters.-# The below works for Postgresql.-#-# import Data.HashMap.Strict as H-# import Data.Aeson.Types as AT-# #ifndef DEVELOPMENT-# import qualified Web.Heroku-# #endif-#-#-#-# makeFoundation :: AppConfig DefaultEnv Extra -> Logger -> IO App-# makeFoundation conf setLogger = do-# manager <- newManager def-# s <- staticSite-# hconfig <- loadHerokuConfig-# dbconf <- withYamlEnvironment "config/postgresql.yml" (appEnv conf)-# (Database.Persist.Store.loadConfig . combineMappings hconfig) >>=-# Database.Persist.Store.applyEnv-# p <- Database.Persist.Store.createPoolConfig (dbconf :: Settings.PersistConfig)-# Database.Persist.Store.runPool dbconf (runMigration migrateAll) p-# return $ App conf setLogger s p manager dbconf-#-# #ifndef DEVELOPMENT-# canonicalizeKey :: (Text, val) -> (Text, val)-# canonicalizeKey ("dbname", val) = ("database", val)-# canonicalizeKey pair = pair-#-# toMapping :: [(Text, Text)] -> AT.Value-# toMapping xs = AT.Object $ M.fromList $ map (\(key, val) -> (key, AT.String val)) xs-# #endif-#-# combineMappings :: AT.Value -> AT.Value -> AT.Value-# combineMappings (AT.Object m1) (AT.Object m2) = AT.Object $ m1 `M.union` m2-# combineMappings _ _ = error "Data.Object is not a Mapping."-#-# loadHerokuConfig :: IO AT.Value-# loadHerokuConfig = do-# #ifdef DEVELOPMENT-# return $ AT.Object M.empty-# #else-# Web.Heroku.dbConnParams >>= return . toMapping . map canonicalizeKey-# #endif--+static-dir: "_env:STATIC_DIR:static"+host: "_env:HOST:*4" # any IPv4 host+port: "_env:PORT:3000"+approot: "_env:APPROOT:http://localhost:3000"+ip-from-header: "_env:IP_FROM_HEADER:false" -# Heroku setup:-# Find the Heroku guide. Roughly:-#-# * sign up for a heroku account and register your ssh key-# * create a new application on the *cedar* stack-#-# * make your Yesod project the git repository for that application-# * create a deploy branch-#-# git checkout -b deploy-#-# Repeat these steps to deploy:-# * add your web executable binary (referenced below) to the git repository-#-# git checkout deploy-# git add ./dist/build/PROJECTNAME/PROJECTNAME-# git commit -m deploy-#-# * push to Heroku+# Optional values with the following production defaults.+# In development, they default to the inverse. #-# git push heroku deploy:master---# Heroku configuration that runs your app-web: ./dist/build/PROJECTNAME/PROJECTNAME production -p $PORT--{-# START_FILE devel.hs #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE PackageImports #-}-import "PROJECTNAME" Application (getApplicationDev)-import Network.Wai.Handler.Warp- (runSettings, defaultSettings, setPort)-import Control.Concurrent (forkIO)-import System.Directory (doesFileExist, removeFile)-import System.Exit (exitSuccess)-import Control.Concurrent (threadDelay)--#ifndef mingw32_HOST_OS-import System.Posix.Signals (installHandler, sigINT, Handler(Catch))-#endif--main :: IO ()-main = do-#ifndef mingw32_HOST_OS- _ <- installHandler sigINT (Catch $ return ()) Nothing-#endif+# development: false+# detailed-logging: false+# should-log-all: false+# reload-templates: false+# mutable-static: false+# skip-combining: false - putStrLn "Starting devel application"- (port, app) <- getApplicationDev- forkIO $ runSettings (setPort port defaultSettings) app- loop+database:+ user: "_env:PGUSER:PROJECTNAME"+ password: "_env:PGPASS:PROJECTNAME"+ host: "_env:PGHOST:localhost"+ port: "_env:PGPORT:5432"+ database: "_env:PGDATABASE:PROJECTNAME"+ poolsize: "_env:PGPOOLSIZE:10" -loop :: IO ()-loop = do- threadDelay 100000- e <- doesFileExist "yesod-devel/devel-terminate"- if e then terminateDevel else loop+copyright: Insert copyright statement here+#analytics: UA-YOURCODE -terminateDevel :: IO ()-terminateDevel = exitSuccess+{-# START_FILE config/test-settings.yml #-}+database:+ database: PROJECTNAME_test {-# START_FILE fay-shared/SharedTypes.hs #-} {-# LANGUAGE NoImplicitPrelude #-}@@ -1120,9 +1029,6 @@ index <- parseInt indexS call (GetFib index) $ setInnerHTML result . T.pack . show -{-# START_FILE messages/en.msg #-}-Hello: Hello- {-# START_FILE static/css/bootstrap.css #-} /*! * Bootstrap v3.2.0 (http://getbootstrap.com)@@ -9087,9 +8993,9 @@ <div id="main" role="main"> ^{pageBody pc} <footer>- #{extraCopyright $ appExtra $ settings master}+ #{appCopyright $ appSettings master} - $maybe analytics <- extraAnalytics $ appExtra $ settings master+ $maybe analytics <- appAnalytics $ appSettings master <script> if(!window.location.href.match(/localhost/)){ window._gaq = [['_setAccount','#{analytics}'],['_trackPageview'],['_trackPageLoadTime']];@@ -9112,7 +9018,7 @@ ^{widget} {-# START_FILE templates/homepage.hamlet #-}-<h1>_{MsgHello}+<h1>Welcome to Yesod! <ol> <li>Now that you have a working project you should use the #@@ -9168,92 +9074,88 @@ color: #990 } -{-# START_FILE test/Handler/HomeSpec.hs #-}-module Handler.HomeSpec- ( spec- ) where+{-# START_FILE test/Handler/CommonSpec.hs #-}+module Handler.CommonSpec (spec) where import TestImport-import qualified Data.List as L spec :: Spec-spec =- ydescribe "These are some example tests" $ do-- yit "loads the index and checks it looks right" $ do- get HomeR+spec = withApp $ do+ describe "robots.txt" $ do+ it "gives a 200" $ do+ get RobotsR statusIs 200- htmlAllContain "h1" "Hello"+ it "has correct User-agent" $ do+ get RobotsR+ bodyContains "User-agent: *"+ describe "favicon.ico" $ do+ it "gives a 200" $ do+ get FaviconR+ statusIs 200 - request $ do- setMethod "POST"- setUrl HomeR- addNonce- fileByLabel "Choose a file" "test/Spec.hs" "text/plain" -- talk about self-reference- byLabel "What's on the file?" "Some Content"+{-# START_FILE test/Handler/HomeSpec.hs #-}+module Handler.HomeSpec (spec) where - statusIs 200- -- more debugging printBody- htmlCount ".message" 1- htmlAllContain ".message" "Some Content"- htmlAllContain ".message" "text/plain"+import TestImport - -- This is a simple example of using a database access in a test. The- -- test will succeed for a fresh scaffolded site with an empty database,- -- but will fail on an existing database with a non-empty user table.- yit "leaves the user table empty" $ do- get HomeR- statusIs 200- users <- runDB $ selectList ([] :: [Filter User]) []- assertEqual "user table empty" 0 $ L.length users+spec :: Spec+spec = withApp $ do+ it "loads the index and checks it looks right" $ do+ get HomeR+ statusIs 200+ htmlAllContain "h1" "Welcome to Yesod" -{-# START_FILE test/Spec.hs #-}-module Main where+ request $ do+ setMethod "POST"+ setUrl HomeR+ addNonce+ fileByLabel "Choose a file" "test/Spec.hs" "text/plain" -- talk about self-reference+ byLabel "What's on the file?" "Some Content" -import Import-import Yesod.Default.Config-import Yesod.Test-import Test.Hspec (hspec)-import Application (makeFoundation)+ statusIs 200+ -- more debugging printBody+ htmlCount ".message" 1+ htmlAllContain ".message" "Some Content"+ htmlAllContain ".message" "text/plain" -import qualified Handler.HomeSpec+ -- This is a simple example of using a database access in a test. The+ -- test will succeed for a fresh scaffolded site with an empty database,+ -- but will fail on an existing database with a non-empty user table.+ it "leaves the user table empty" $ do+ get HomeR+ statusIs 200+ users <- runDB $ selectList ([] :: [Filter User]) []+ assertEqual "user table empty" 0 $ length users -main :: IO ()-main = do- conf <- Yesod.Default.Config.loadConfig $ (configSettings Testing)- { csParseExtra = parseExtra- }- foundation <- makeFoundation conf- hspec $ do- yesodSpec foundation $ do- Handler.HomeSpec.spec+{-# START_FILE test/Spec.hs #-}+{-# OPTIONS_GHC -F -pgmF hspec-discover #-} {-# START_FILE test/TestImport.hs #-} module TestImport- ( module Yesod.Test- , module Model- , module Foundation- , module Database.Persist- , module Prelude- , runDB- , Spec- , Example+ ( module TestImport+ , module X ) where -import Yesod.Test-import Database.Persist hiding (get)-import Database.Persist.Sql (SqlPersistM, runSqlPersistMPool)-import Control.Monad.IO.Class (liftIO)-import Prelude--import Foundation-import Model--type Spec = YesodSpec App-type Example = YesodExample App+import Application (makeFoundation)+import ClassyPrelude as X+import Database.Persist as X hiding (get)+import Database.Persist.Sql (SqlPersistM, runSqlPersistMPool)+import Foundation as X+import Model as X+import Test.Hspec as X+import Yesod.Default.Config2 (ignoreEnv, loadAppSettings)+import Yesod.Test as X -runDB :: SqlPersistM a -> Example a+runDB :: SqlPersistM a -> YesodExample App a runDB query = do- pool <- fmap connPool getTestYesod+ pool <- fmap appConnPool getTestYesod liftIO $ runSqlPersistMPool query pool++withApp :: SpecWith App -> Spec+withApp = before $ do+ settings <- loadAppSettings+ ["config/test-settings.yml", "config/settings.yml"]+ []+ ignoreEnv+ makeFoundation settings
hsfiles/postgres.hsfiles view
@@ -28,32 +28,31 @@ {-# START_FILE Application.hs #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Application- ( makeApplication- , getApplicationDev+ ( getApplicationDev+ , appMain+ , develMain , makeFoundation ) where +import Control.Monad.Logger (liftLoc, runLoggingT)+import Database.Persist.Postgresql (createPostgresqlPool, pgConnStr,+ pgPoolSize, runSqlPool) import Import-import Yesod.Auth-import Yesod.Default.Config-import Yesod.Default.Main-import Yesod.Default.Handlers-import Network.Wai.Middleware.RequestLogger- ( mkRequestLogger, outputFormat, OutputFormat (..), IPAddrSource (..), destination- )-import qualified Network.Wai.Middleware.RequestLogger as RequestLogger-import qualified Database.Persist-import Database.Persist.Sql (runMigration)-import Database.Persist.Postgresql (createPostgresqlPool, pgConnStr, pgPoolSize)-import Network.HTTP.Client.Conduit (newManager)-import Control.Monad.Logger (runLoggingT)-import System.Log.FastLogger (newStdoutLoggerSet, defaultBufSize)-import Network.Wai.Logger (clockDateCacher)-import Data.Default (def)-import Yesod.Core.Types (loggerSet, Logger (Logger))+import Language.Haskell.TH.Syntax (qLocation)+import Network.Wai.Handler.Warp (Settings, defaultSettings,+ defaultShouldDisplayException,+ runSettings, setHost,+ setOnException, setPort)+import Network.Wai.Middleware.RequestLogger (Destination (Logger),+ IPAddrSource (..),+ OutputFormat (..), destination,+ mkRequestLogger, outputFormat)+import System.Log.FastLogger (defaultBufSize, newStdoutLoggerSet,+ toLogStr) -- Import all relevant handler modules here. -- Don't forget to add new modules to your cabal file!+import Handler.Common import Handler.Home -- This line actually creates our YesodDispatch instance. It is the second half@@ -61,112 +60,136 @@ -- comments there for more details. mkYesodDispatch "App" resourcesApp --- This function allocates resources (such as a database connection pool),--- performs initialization and creates a WAI application. This is also the--- place to put your migrate statements to have automatic database+-- | This function allocates resources (such as a database connection pool),+-- performs initialization and return a foundation datatype value. This is also+-- the place to put your migrate statements to have automatic database -- migrations handled by Yesod.-makeApplication :: AppConfig DefaultEnv Extra -> IO (Application, LogFunc)-makeApplication conf = do- foundation <- makeFoundation conf+makeFoundation :: AppSettings -> IO App+makeFoundation appSettings = do+ -- Some basic initializations: HTTP connection manager, logger, and static+ -- subsite.+ appHttpManager <- newManager+ appLogger <- newStdoutLoggerSet defaultBufSize >>= makeYesodLogger+ appStatic <-+ (if appMutableStatic appSettings then staticDevel else static)+ (appStaticDir appSettings) - -- Initialize the logging middleware+ -- We need a log function to create a connection pool. We need a connection+ -- pool to create our foundation. And we need our foundation to get a+ -- logging function. To get out of this loop, we initially create a+ -- temporary foundation without a real connection pool, get a log function+ -- from there, and then create the real foundation.+ let mkFoundation appConnPool = App {..}+ tempFoundation = mkFoundation $ error "connPool forced in tempFoundation"+ logFunc = messageLoggerSource tempFoundation appLogger++ -- Create the database connection pool+ pool <- flip runLoggingT logFunc $ createPostgresqlPool+ (pgConnStr $ appDatabaseConf appSettings)+ (pgPoolSize $ appDatabaseConf appSettings)++ -- Perform database migration using our application's logging settings.+ runLoggingT (runSqlPool (runMigration migrateAll) pool) logFunc++ -- Return the foundation+ return $ mkFoundation pool++-- | Convert our foundation to a WAI Application by calling @toWaiAppPlain@ and+-- applyng some additional middlewares.+makeApplication :: App -> IO Application+makeApplication foundation = do logWare <- mkRequestLogger def { outputFormat =- if development+ if appDetailedRequestLogging $ appSettings foundation then Detailed True- else Apache FromSocket- , destination = RequestLogger.Logger $ loggerSet $ appLogger foundation+ else Apache+ (if appIpFromHeader $ appSettings foundation+ then FromFallback+ else FromSocket)+ , destination = Logger $ loggerSet $ appLogger foundation } -- Create the WAI application and apply middlewares- app <- toWaiAppPlain foundation- let logFunc = messageLoggerSource foundation (appLogger foundation)- return (logWare $ defaultMiddlewaresNoLogging app, logFunc)+ appPlain <- toWaiAppPlain foundation+ return $ logWare $ defaultMiddlewaresNoLogging appPlain --- | Loads up any necessary settings, creates your foundation datatype, and--- performs some initialization.-makeFoundation :: AppConfig DefaultEnv Extra -> IO App-makeFoundation conf = do- manager <- newManager- s <- staticSite- dbconf <- withYamlEnvironment "config/postgresql.yml" (appEnv conf)- Database.Persist.loadConfig >>=- Database.Persist.applyEnv+-- | Warp settings for the given foundation value.+warpSettings :: App -> Settings+warpSettings foundation =+ setPort (appPort $ appSettings foundation)+ $ setHost (appHost $ appSettings foundation)+ $ setOnException (\_req e ->+ when (defaultShouldDisplayException e) $ messageLoggerSource+ foundation+ (appLogger foundation)+ $(qLocation >>= liftLoc)+ "yesod"+ LevelError+ (toLogStr $ "Exception from Warp: " ++ show e))+ defaultSettings - loggerSet' <- newStdoutLoggerSet defaultBufSize- (getter, _) <- clockDateCacher+-- | For yesod devel, return the Warp settings and WAI Application.+getApplicationDev :: IO (Settings, Application)+getApplicationDev = do+ settings <- loadAppSettings [configSettingsYml] [] useEnv+ foundation <- makeFoundation settings+ app <- makeApplication foundation+ wsettings <- getDevSettings $ warpSettings foundation+ return (wsettings, app) - let logger = Yesod.Core.Types.Logger loggerSet' getter- mkFoundation p = App- { settings = conf- , getStatic = s- , connPool = p- , httpManager = manager- , persistConfig = dbconf- , appLogger = logger- }- tempFoundation = mkFoundation $ error "connPool forced in tempFoundation"- logFunc = messageLoggerSource tempFoundation logger+-- | main function for use by yesod devel+develMain :: IO ()+develMain = develMainHelper getApplicationDev - p <- flip runLoggingT logFunc- $ createPostgresqlPool (pgConnStr dbconf) (pgPoolSize dbconf)- let foundation = mkFoundation p+-- | The @main@ function for an executable running this site.+appMain :: IO ()+appMain = do+ -- Get the settings from all relevant sources+ settings <- loadAppSettingsArgs+ -- fall back to compile-time values, set to [] to require values at runtime+ [configSettingsYmlValue] - -- Perform database migration using our application's logging settings.- flip runLoggingT logFunc- (Database.Persist.runPool dbconf (runMigration migrateAll) p)+ -- allow environment variables to override+ useEnv - return foundation+ -- Generate the foundation from the settings+ foundation <- makeFoundation settings --- for yesod devel-getApplicationDev :: IO (Int, Application)-getApplicationDev =- defaultDevelApp loader (fmap fst . makeApplication)- where- loader = Yesod.Default.Config.loadConfig (configSettings Development)- { csParseExtra = parseExtra- }+ -- Generate a WAI Application from the foundation+ app <- makeApplication foundation + -- Run the application with Warp+ runSettings (warpSettings foundation) app+ {-# START_FILE Foundation.hs #-} module Foundation where -import Prelude-import Yesod-import Yesod.Static-import Yesod.Auth-import Yesod.Auth.BrowserId-import Yesod.Default.Config-import Yesod.Default.Util (addStaticContentExternal)-import Network.HTTP.Client.Conduit (Manager, HasHttpManager (getHttpManager))-import qualified Settings-import Settings.Development (development)-import qualified Database.Persist-import Database.Persist.Sql (SqlBackend)-import Settings.StaticFiles-import Settings (widgetFile, Extra (..))+import ClassyPrelude.Yesod+import Database.Persist.Sql (ConnectionPool, runSqlPool) import Model-import Text.Jasmine (minifym)-import Text.Hamlet (hamletFile)-import Yesod.Core.Types (Logger)+import Settings+import Settings.StaticFiles+import Text.Hamlet (hamletFile)+import Text.Jasmine (minifym)+import Yesod.Auth+import Yesod.Auth.BrowserId (authBrowserId)+import Yesod.Core.Types (Logger)+import Yesod.Default.Util (addStaticContentExternal) --- | The site argument for your application. This can be a good place to+-- | The foundation datatype for your application. This can be a good place to -- keep settings and values requiring initialization before your application -- starts running, such as database connections. Every handler will have -- access to the data present here. data App = App- { settings :: AppConfig DefaultEnv Extra- , getStatic :: Static -- ^ Settings for static file serving.- , connPool :: Database.Persist.PersistConfigPool Settings.PersistConf -- ^ Database connection pool.- , httpManager :: Manager- , persistConfig :: Settings.PersistConf- , appLogger :: Logger+ { appSettings :: AppSettings+ , appStatic :: Static -- ^ Settings for static file serving.+ , appConnPool :: ConnectionPool -- ^ Database connection pool.+ , appHttpManager :: Manager+ , appLogger :: Logger } instance HasHttpManager App where- getHttpManager = httpManager---- Set up i18n messages. See the message folder.-mkMessage "App" "messages" "en"+ getHttpManager = appHttpManager -- This is where we define all of the routes in our application. For a full -- explanation of the syntax, please see:@@ -177,12 +200,15 @@ -- explanation for this split. mkYesodData "App" $(parseRoutesFile "config/routes") +-- | A convenient synonym for creating forms. type Form x = Html -> MForm (HandlerT App IO) (FormResult x, Widget) -- Please see the documentation for the Yesod typeclass. There are a number -- of settings which can be configured by overriding methods here. instance Yesod App where- approot = ApprootMaster $ appRoot . settings+ -- Controls the base of generated URLs. For more information on modifying,+ -- see: https://github.com/yesodweb/yesod/wiki/Overriding-approot+ approot = ApprootMaster $ appRoot . appSettings -- Store session data on the client in encrypted cookies, -- default session idle timeout is 120 minutes@@ -205,12 +231,6 @@ $(widgetFile "default-layout") withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet") - -- This is done to provide an optimization for serving static files from- -- a separate domain. Please see the staticRoot setting in Settings.hs- urlRenderOverride y (StaticR s) =- Just $ uncurry (joinPath y (Settings.staticRoot $ settings y)) $ renderRoute s- urlRenderOverride _ _ = Nothing- -- The page to be redirected to when authentication is required. authRoute _ = Just $ AuthR LoginR @@ -225,30 +245,38 @@ -- 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 =- addStaticContentExternal minifym genFileName Settings.staticDir (StaticR . flip StaticRoute [])+ addStaticContent ext mime content = do+ master <- getYesod+ let staticDir = appStaticDir $ appSettings master+ addStaticContentExternal+ minifym+ genFileName+ staticDir+ (StaticR . flip StaticRoute [])+ ext+ mime+ content where -- Generate a unique filename based on the content itself- genFileName lbs- | development = "autogen-" ++ base64md5 lbs- | otherwise = base64md5 lbs-- -- Place Javascript at bottom of the body tag so the rest of the page loads first- jsLoader _ = BottomOfBody+ genFileName lbs = "autogen-" ++ base64md5 lbs -- What messages should be logged. The following includes all messages when -- in development, and warnings and errors in production.- shouldLog _ _source level =- development || level == LevelWarn || level == LevelError+ shouldLog app _source level =+ appShouldLogAll (appSettings app)+ || level == LevelWarn+ || level == LevelError makeLogger = return . appLogger -- How to run database actions. instance YesodPersist App where type YesodPersistBackend App = SqlBackend- runDB = defaultRunDB persistConfig connPool+ runDB action = do+ master <- getYesod+ runSqlPool action $ appConnPool master instance YesodPersistRunner App where- getDBRunner = defaultGetDBRunner connPool+ getDBRunner = defaultGetDBRunner appConnPool instance YesodAuth App where type AuthId App = UserId@@ -257,6 +285,8 @@ loginDest _ = HomeR -- Where to send a user after logout logoutDest _ = HomeR+ -- Override the above two destinations when a Referer: header is present+ redirectToReferer _ = True getAuthId creds = runDB $ do x <- getBy $ UniqueUser $ credsIdent creds@@ -271,7 +301,7 @@ -- You can add other plugins like BrowserID, email or OAuth here authPlugins _ = [authBrowserId def] - authHttpManager = httpManager+ authHttpManager = getHttpManager instance YesodAuthPersist App @@ -280,23 +310,38 @@ instance RenderMessage App FormMessage where renderMessage _ _ = defaultFormMessage --- | Get the 'Extra' value, used to hold data from the settings.yml file.-getExtra :: Handler Extra-getExtra = fmap (appExtra . settings) getYesod---- Note: previous versions of the scaffolding included a deliver function to--- send emails. Unfortunately, there are too many different options for us to--- give a reasonable default. Instead, the information is available on the--- wiki:+-- Note: Some functionality previously present in the scaffolding has been+-- moved to documentation in the Wiki. Following are some hopefully helpful+-- links: -- -- https://github.com/yesodweb/yesod/wiki/Sending-email+-- https://github.com/yesodweb/yesod/wiki/Serve-static-files-from-a-separate-domain+-- https://github.com/yesodweb/yesod/wiki/i18n-messages-in-the-scaffolding +{-# START_FILE Handler/Common.hs #-}+-- | Common handler functions.+module Handler.Common where++import Data.FileEmbed (embedFile)+import Import++-- These handlers embed files in the executable at compile time to avoid a+-- runtime dependency, and for efficiency.++getFaviconR :: Handler TypedContent+getFaviconR = return $ TypedContent "image/x-icon"+ $ toContent $(embedFile "config/favicon.ico")++getRobotsR :: Handler TypedContent+getRobotsR = return $ TypedContent typePlain+ $ toContent $(embedFile "config/robots.txt")+ {-# START_FILE Handler/Home.hs #-} module Handler.Home where import Import-import Yesod.Form.Bootstrap3- ( BootstrapFormLayout (..), renderBootstrap3, withSmallInput )+import Yesod.Form.Bootstrap3 (BootstrapFormLayout (..), renderBootstrap3,+ withSmallInput) -- This is a handler function for the GET request method on the HomeR -- resource pattern. All of your resource patterns are defined in@@ -338,40 +383,20 @@ ( module Import ) where -import Prelude as Import hiding (head, init, last,- readFile, tail, writeFile)-import Yesod as Import hiding (Route (..))--import Control.Applicative as Import (pure, (<$>), (<*>))-import Data.Text as Import (Text)--import Foundation as Import-import Model as Import-import Settings as Import-import Settings.Development as Import-import Settings.StaticFiles as Import--#if __GLASGOW_HASKELL__ >= 704-import Data.Monoid as Import- (Monoid (mappend, mempty, mconcat),- (<>))-#else-import Data.Monoid as Import- (Monoid (mappend, mempty, mconcat))--infixr 5 <>-(<>) :: Monoid m => m -> m -> m-(<>) = mappend-#endif+import ClassyPrelude.Yesod as Import+import Foundation as Import+import Model as Import+import Settings as Import+import Settings.StaticFiles as Import+import Yesod.Auth as Import+import Yesod.Core.Types as Import (loggerSet)+import Yesod.Default.Config2 as Import {-# START_FILE Model.hs #-} module Model where -import Yesod-import Data.Text (Text)+import ClassyPrelude.Yesod import Database.Persist.Quasi-import Data.Typeable (Typeable)-import Prelude -- You can define all of your database entities in the entities file. -- You can find more information on persistent and how to declare entities@@ -401,7 +426,7 @@ Model Settings Settings.StaticFiles- Settings.Development+ Handler.Common Handler.Home if flag(dev) || flag(library-only)@@ -425,17 +450,21 @@ DeriveDataTypeable ViewPatterns TupleSections+ RecordWildCards build-depends: base >= 4 && < 5- , yesod >= 1.4.0 && < 1.5+ , yesod >= 1.4.1 && < 1.5 , yesod-core >= 1.4.0 && < 1.5 , yesod-auth >= 1.4.0 && < 1.5- , yesod-static >= 1.4.0 && < 1.5+ , yesod-static >= 1.4.0.3 && < 1.5 , yesod-form >= 1.4.0 && < 1.5+ , classy-prelude >= 0.10.2+ , classy-prelude-conduit >= 0.10.2+ , classy-prelude-yesod >= 0.10.2 , bytestring >= 0.9 && < 0.11 , text >= 0.11 && < 2.0 , persistent >= 2.0 && < 2.2- , persistent-postgresql >= 2.0 && < 2.2+ , persistent-postgresql >= 2.1.1 && < 2.2 , persistent-template >= 2.0 && < 2.2 , template-haskell , shakespeare >= 2.0 && < 2.1@@ -452,10 +481,12 @@ , monad-logger >= 0.3 && < 0.4 , fast-logger >= 2.2 && < 2.3 , wai-logger >= 2.2 && < 2.3-- -- see https://github.com/yesodweb/yesod/issues/814- if !os(windows)- build-depends: unix+ , file-embed+ , safe+ , unordered-containers+ , containers+ , vector+ , time executable PROJECTNAME if flag(library-only)@@ -463,9 +494,7 @@ main-is: main.hs hs-source-dirs: app- build-depends: base- , PROJECTNAME- , yesod+ build-depends: base, PROJECTNAME ghc-options: -threaded -O2 -rtsopts -with-rtsopts=-N @@ -493,7 +522,7 @@ build-depends: base , PROJECTNAME- , yesod-test >= 1.4 && < 1.5+ , yesod-test >= 1.4.2 && < 1.5 , yesod-core , yesod , persistent@@ -502,6 +531,8 @@ , monad-logger , transformers , hspec+ , classy-prelude+ , classy-prelude-yesod {-# START_FILE Settings.hs #-} -- | Settings are centralized, as much as possible, into this file. This@@ -511,45 +542,81 @@ -- declared in the Foundation.hs file. module Settings where -import Prelude-import Text.Shakespeare.Text (st)-import Language.Haskell.TH.Syntax+import ClassyPrelude.Yesod+import Control.Exception (throw)+import Data.Aeson (Result (..), fromJSON, withObject, (.!=),+ (.:?))+import Data.FileEmbed (embedFile)+import Data.Yaml (decodeEither') import Database.Persist.Postgresql (PostgresConf)-import Yesod.Default.Config-import Yesod.Default.Util-import Data.Text (Text)-import Data.Yaml-import Control.Applicative-import Settings.Development-import Data.Default (def)-import Text.Hamlet+import Language.Haskell.TH.Syntax (Exp, Name, Q)+import Network.Wai.Handler.Warp (HostPreference)+import Yesod.Default.Config2 (applyEnvValue, configSettingsYml)+import Yesod.Default.Util (WidgetFileSettings, widgetFileNoReload,+ widgetFileReload) --- | Which Persistent backend this site is using.-type PersistConf = PostgresConf+-- | Runtime settings to configure this application. These settings can be+-- loaded from various sources: defaults, environment variables, config files,+-- theoretically even a database.+data AppSettings = AppSettings+ { appStaticDir :: String+ -- ^ Directory from which to serve static files.+ , appDatabaseConf :: PostgresConf+ -- ^ Configuration settings for accessing the database.+ , appRoot :: Text+ -- ^ Base for all generated URLs.+ , appHost :: HostPreference+ -- ^ Host/interface the server should bind to.+ , appPort :: Int+ -- ^ Port to listen on+ , appIpFromHeader :: Bool+ -- ^ Get the IP address from the header when logging. Useful when sitting+ -- behind a reverse proxy. --- Static setting below. Changing these requires a recompile+ , appDetailedRequestLogging :: Bool+ -- ^ Use detailed request logging system+ , appShouldLogAll :: Bool+ -- ^ Should all log messages be displayed?+ , appReloadTemplates :: Bool+ -- ^ Use the reload version of templates+ , appMutableStatic :: Bool+ -- ^ Assume that files in the static dir may change after compilation+ , appSkipCombining :: Bool+ -- ^ Perform no stylesheet/script combining --- | 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-staticDir = "static"+ -- Example app-specific configuration values.+ , appCopyright :: Text+ -- ^ Copyright text to appear in the footer of the page+ , appAnalytics :: Maybe Text+ -- ^ Google Analytics code+ } --- | The base URL for your static files. As you can see by the default--- value, this can simply be "static" appended to your application root.--- A powerful optimization can be serving static files from a separate--- domain name. This allows you to use a web server optimized for static--- files, more easily set expires and cache values, and avoid possibly--- costly transference of cookies on static files. For more information,--- please see:--- http://code.google.com/speed/page-speed/docs/request.html#ServeFromCookielessDomain------ If you change the resource pattern for StaticR in Foundation.hs, you will--- have to make a corresponding change here.------ To see how this value is used, see urlRenderOverride in Foundation.hs-staticRoot :: AppConfig DefaultEnv x -> Text-staticRoot conf = [st|#{appRoot conf}/static|]+instance FromJSON AppSettings where+ parseJSON = withObject "AppSettings" $ \o -> do+ let defaultDev =+#if DEVELOPMENT+ True+#else+ False+#endif+ appStaticDir <- o .: "static-dir"+ appDatabaseConf <- o .: "database"+ appRoot <- o .: "approot"+ appHost <- fromString <$> o .: "host"+ appPort <- o .: "port"+ appIpFromHeader <- o .: "ip-from-header" + appDetailedRequestLogging <- o .:? "detailed-logging" .!= defaultDev+ appShouldLogAll <- o .:? "should-log-all" .!= defaultDev+ appReloadTemplates <- o .:? "reload-templates" .!= defaultDev+ appMutableStatic <- o .:? "mutable-static" .!= defaultDev+ appSkipCombining <- o .:? "skip-combining" .!= defaultDev++ appCopyright <- o .: "copyright"+ appAnalytics <- o .:? "analytics"++ return AppSettings {..}+ -- | Settings for 'widgetFile', such as which template languages to support and -- default Hamlet settings. --@@ -558,81 +625,62 @@ -- https://github.com/yesodweb/yesod/wiki/Overriding-widgetFile widgetFileSettings :: WidgetFileSettings widgetFileSettings = def- { wfsHamletSettings = defaultHamletSettings- { hamletNewlines = AlwaysNewlines- }- } +-- | How static files should be combined.+combineSettings :: CombineSettings+combineSettings = def+ -- The rest of this file contains settings which rarely need changing by a -- user. widgetFile :: String -> Q Exp-widgetFile = (if development then widgetFileReload- else widgetFileNoReload)+widgetFile = (if appReloadTemplates compileTimeAppSettings+ then widgetFileReload+ else widgetFileNoReload) widgetFileSettings -data Extra = Extra- { extraCopyright :: Text- , extraAnalytics :: Maybe Text -- ^ Google Analytics- } deriving Show+-- | Raw bytes at compile time of @config/settings.yml@+configSettingsYmlBS :: ByteString+configSettingsYmlBS = $(embedFile configSettingsYml) -parseExtra :: DefaultEnv -> Object -> Parser Extra-parseExtra _ o = Extra- <$> o .: "copyright"- <*> o .:? "analytics"+-- | @config/settings.yml@, parsed to a @Value@.+configSettingsYmlValue :: Value+configSettingsYmlValue = either throw id $ decodeEither' configSettingsYmlBS -{-# START_FILE Settings/Development.hs #-}-module Settings.Development where+-- | A version of @AppSettings@ parsed at compile time from @config/settings.yml@.+compileTimeAppSettings :: AppSettings+compileTimeAppSettings =+ case fromJSON $ applyEnvValue False mempty configSettingsYmlValue of+ Error e -> error e+ Success settings -> settings -import Prelude+-- The following two functions can be used to combine multiple CSS or JS files+-- at compile time to decrease the number of http requests.+-- Sample usage (inside a Widget):+--+-- > $(combineStylesheets 'StaticR [style1_css, style2_css]) -development :: Bool-development =-#if DEVELOPMENT- True-#else- False-#endif+combineStylesheets :: Name -> [Route Static] -> Q Exp+combineStylesheets = combineStylesheets'+ (appSkipCombining compileTimeAppSettings)+ combineSettings -production :: Bool-production = not development+combineScripts :: Name -> [Route Static] -> Q Exp+combineScripts = combineScripts'+ (appSkipCombining compileTimeAppSettings)+ combineSettings {-# START_FILE Settings/StaticFiles.hs #-} module Settings.StaticFiles where -import Prelude (IO)-import Yesod.Static-import qualified Yesod.Static as Static-import Settings (staticDir)-import Settings.Development-import Language.Haskell.TH (Q, Exp, Name)-import Data.Default (def)---- | use this to create your static file serving site-staticSite :: IO Static.Static-staticSite = if development then Static.staticDevel staticDir- else Static.static staticDir+import Settings (appStaticDir, compileTimeAppSettings)+import Yesod.Static (staticFiles) -- | This generates easy references to files in the static directory at compile time, -- giving you compile-time verification that referenced files exist. -- Warning: any files added to your static directory during run-time can't be -- accessed this way. You'll have to use their FilePath or URL to access them.-$(staticFiles Settings.staticDir)--combineSettings :: CombineSettings-combineSettings = def---- The following two functions can be used to combine multiple CSS or JS files--- at compile time to decrease the number of http requests.--- Sample usage (inside a Widget):------ > $(combineStylesheets 'StaticR [style1_css, style2_css])--combineStylesheets :: Name -> [Route Static] -> Q Exp-combineStylesheets = combineStylesheets' development combineSettings--combineScripts :: Name -> [Route Static] -> Q Exp-combineScripts = combineScripts' development combineSettings+staticFiles (appStaticDir compileTimeAppSettings) {-# START_FILE app/DevelMain.hs #-} -- | Development version to be run inside GHCi.@@ -699,19 +747,24 @@ start :: MVar () -- ^ Written to when the thread is killed. -> IO ThreadId start done = do- (port,app) <- getApplicationDev- forkIO (finally (runSettings (setPort port defaultSettings) app)+ (settings,app) <- getApplicationDev+ forkIO (finally (runSettings settings app) (putMVar done ())) +{-# START_FILE app/devel.hs #-}+{-# LANGUAGE PackageImports #-}+import "PROJECTNAME" Application (develMain)+import Prelude (IO)++main :: IO ()+main = develMain+ {-# START_FILE app/main.hs #-}-import Prelude (IO)-import Yesod.Default.Config (fromArgs)-import Yesod.Default.Main (defaultMainLog)-import Settings (parseExtra)-import Application (makeApplication)+import Prelude (IO)+import Application (appMain) main :: IO ()-main = defaultMainLog (fromArgs parseExtra) makeApplication+main = appMain {-# START_FILE BASE64 config/favicon.ico #-} AAABAAIAEBAAAAEAIABoBAAAJgAAABAQAgABAAEAsAAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAA@@ -816,46 +869,11 @@ -- By default this file is used in Model.hs (which is imported by Foundation.hs) -{-# START_FILE config/postgresql.yml #-}-# NOTE: These settings can be overridden by environment variables as well, in-# particular:-#-# PGHOST-# PGPORT-# PGUSER-# PGPASS-# PGDATABASE--Default: &defaults- user: PROJECTNAME- password: PROJECTNAME- host: localhost- port: 5432- database: PROJECTNAME- poolsize: 10--Development:- <<: *defaults--Testing:- database: PROJECTNAME_test- <<: *defaults--Staging:- database: PROJECTNAME_staging- poolsize: 100- <<: *defaults--Production:- database: PROJECTNAME_production- poolsize: 100- <<: *defaults- {-# START_FILE config/robots.txt #-} User-agent: * {-# START_FILE config/routes #-}-/static StaticR Static getStatic+/static StaticR Static appStatic /auth AuthR Auth getAuth /favicon.ico FaviconR GET@@ -864,155 +882,36 @@ / HomeR GET POST {-# START_FILE config/settings.yml #-}-Default: &defaults- host: "*4" # any IPv4 host- port: 3000- approot: "http://localhost:3000"- copyright: Insert copyright statement here- #analytics: UA-YOURCODE--Development:- <<: *defaults--Testing:- <<: *defaults--Staging:- <<: *defaults--Production:- #approot: "http://www.example.com"- <<: *defaults--{-# START_FILE deploy/Procfile #-}-# Free deployment to Heroku.-#-# !! Warning: You must use a 64 bit machine to compile !!-#-# This could mean using a virtual machine. Give your VM as much memory as you can to speed up linking.-#-# Basic Yesod setup:-#-# * Move this file out of the deploy directory and into your root directory-#-# mv deploy/Procfile ./-#-# * Create an empty package.json-# echo '{ "name": "PROJECTNAME", "version": "0.0.1", "dependencies": {} }' >> package.json-#-# Postgresql Yesod setup:-#-# * add dependencies on the "heroku", "aeson" and "unordered-containers" packages in your cabal file-#-# * add code in Application.hs to use the heroku package and load the connection parameters.-# The below works for Postgresql.-#-# import Data.HashMap.Strict as H-# import Data.Aeson.Types as AT-# #ifndef DEVELOPMENT-# import qualified Web.Heroku-# #endif-#-#-#-# makeFoundation :: AppConfig DefaultEnv Extra -> Logger -> IO App-# makeFoundation conf setLogger = do-# manager <- newManager def-# s <- staticSite-# hconfig <- loadHerokuConfig-# dbconf <- withYamlEnvironment "config/postgresql.yml" (appEnv conf)-# (Database.Persist.Store.loadConfig . combineMappings hconfig) >>=-# Database.Persist.Store.applyEnv-# p <- Database.Persist.Store.createPoolConfig (dbconf :: Settings.PersistConfig)-# Database.Persist.Store.runPool dbconf (runMigration migrateAll) p-# return $ App conf setLogger s p manager dbconf-#-# #ifndef DEVELOPMENT-# canonicalizeKey :: (Text, val) -> (Text, val)-# canonicalizeKey ("dbname", val) = ("database", val)-# canonicalizeKey pair = pair-#-# toMapping :: [(Text, Text)] -> AT.Value-# toMapping xs = AT.Object $ M.fromList $ map (\(key, val) -> (key, AT.String val)) xs-# #endif-#-# combineMappings :: AT.Value -> AT.Value -> AT.Value-# combineMappings (AT.Object m1) (AT.Object m2) = AT.Object $ m1 `M.union` m2-# combineMappings _ _ = error "Data.Object is not a Mapping."-#-# loadHerokuConfig :: IO AT.Value-# loadHerokuConfig = do-# #ifdef DEVELOPMENT-# return $ AT.Object M.empty-# #else-# Web.Heroku.dbConnParams >>= return . toMapping . map canonicalizeKey-# #endif--+static-dir: "_env:STATIC_DIR:static"+host: "_env:HOST:*4" # any IPv4 host+port: "_env:PORT:3000"+approot: "_env:APPROOT:http://localhost:3000"+ip-from-header: "_env:IP_FROM_HEADER:false" -# Heroku setup:-# Find the Heroku guide. Roughly:-#-# * sign up for a heroku account and register your ssh key-# * create a new application on the *cedar* stack-#-# * make your Yesod project the git repository for that application-# * create a deploy branch-#-# git checkout -b deploy-#-# Repeat these steps to deploy:-# * add your web executable binary (referenced below) to the git repository-#-# git checkout deploy-# git add ./dist/build/PROJECTNAME/PROJECTNAME-# git commit -m deploy-#-# * push to Heroku+# Optional values with the following production defaults.+# In development, they default to the inverse. #-# git push heroku deploy:master---# Heroku configuration that runs your app-web: ./dist/build/PROJECTNAME/PROJECTNAME production -p $PORT--{-# START_FILE devel.hs #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE PackageImports #-}-import "PROJECTNAME" Application (getApplicationDev)-import Network.Wai.Handler.Warp- (runSettings, defaultSettings, setPort)-import Control.Concurrent (forkIO)-import System.Directory (doesFileExist, removeFile)-import System.Exit (exitSuccess)-import Control.Concurrent (threadDelay)--#ifndef mingw32_HOST_OS-import System.Posix.Signals (installHandler, sigINT, Handler(Catch))-#endif--main :: IO ()-main = do-#ifndef mingw32_HOST_OS- _ <- installHandler sigINT (Catch $ return ()) Nothing-#endif-- putStrLn "Starting devel application"- (port, app) <- getApplicationDev- forkIO $ runSettings (setPort port defaultSettings) app- loop+# development: false+# detailed-logging: false+# should-log-all: false+# reload-templates: false+# mutable-static: false+# skip-combining: false -loop :: IO ()-loop = do- threadDelay 100000- e <- doesFileExist "yesod-devel/devel-terminate"- if e then terminateDevel else loop+database:+ user: "_env:PGUSER:PROJECTNAME"+ password: "_env:PGPASS:PROJECTNAME"+ host: "_env:PGHOST:localhost"+ port: "_env:PGPORT:5432"+ database: "_env:PGDATABASE:PROJECTNAME"+ poolsize: "_env:PGPOOLSIZE:10" -terminateDevel :: IO ()-terminateDevel = exitSuccess+copyright: Insert copyright statement here+#analytics: UA-YOURCODE -{-# START_FILE messages/en.msg #-}-Hello: Hello+{-# START_FILE config/test-settings.yml #-}+database:+ database: PROJECTNAME_test {-# START_FILE static/css/bootstrap.css #-} /*!@@ -8978,9 +8877,9 @@ <div id="main" role="main"> ^{pageBody pc} <footer>- #{extraCopyright $ appExtra $ settings master}+ #{appCopyright $ appSettings master} - $maybe analytics <- extraAnalytics $ appExtra $ settings master+ $maybe analytics <- appAnalytics $ appSettings master <script> if(!window.location.href.match(/localhost/)){ window._gaq = [['_setAccount','#{analytics}'],['_trackPageview'],['_trackPageLoadTime']];@@ -9003,7 +8902,7 @@ ^{widget} {-# START_FILE templates/homepage.hamlet #-}-<h1>_{MsgHello}+<h1>Welcome to Yesod! <ol> <li>Now that you have a working project you should use the #@@ -9054,92 +8953,88 @@ color: #990 } -{-# START_FILE test/Handler/HomeSpec.hs #-}-module Handler.HomeSpec- ( spec- ) where+{-# START_FILE test/Handler/CommonSpec.hs #-}+module Handler.CommonSpec (spec) where import TestImport-import qualified Data.List as L spec :: Spec-spec =- ydescribe "These are some example tests" $ do-- yit "loads the index and checks it looks right" $ do- get HomeR+spec = withApp $ do+ describe "robots.txt" $ do+ it "gives a 200" $ do+ get RobotsR statusIs 200- htmlAllContain "h1" "Hello"+ it "has correct User-agent" $ do+ get RobotsR+ bodyContains "User-agent: *"+ describe "favicon.ico" $ do+ it "gives a 200" $ do+ get FaviconR+ statusIs 200 - request $ do- setMethod "POST"- setUrl HomeR- addNonce- fileByLabel "Choose a file" "test/Spec.hs" "text/plain" -- talk about self-reference- byLabel "What's on the file?" "Some Content"+{-# START_FILE test/Handler/HomeSpec.hs #-}+module Handler.HomeSpec (spec) where - statusIs 200- -- more debugging printBody- htmlCount ".message" 1- htmlAllContain ".message" "Some Content"- htmlAllContain ".message" "text/plain"+import TestImport - -- This is a simple example of using a database access in a test. The- -- test will succeed for a fresh scaffolded site with an empty database,- -- but will fail on an existing database with a non-empty user table.- yit "leaves the user table empty" $ do- get HomeR- statusIs 200- users <- runDB $ selectList ([] :: [Filter User]) []- assertEqual "user table empty" 0 $ L.length users+spec :: Spec+spec = withApp $ do+ it "loads the index and checks it looks right" $ do+ get HomeR+ statusIs 200+ htmlAllContain "h1" "Welcome to Yesod" -{-# START_FILE test/Spec.hs #-}-module Main where+ request $ do+ setMethod "POST"+ setUrl HomeR+ addNonce+ fileByLabel "Choose a file" "test/Spec.hs" "text/plain" -- talk about self-reference+ byLabel "What's on the file?" "Some Content" -import Import-import Yesod.Default.Config-import Yesod.Test-import Test.Hspec (hspec)-import Application (makeFoundation)+ statusIs 200+ -- more debugging printBody+ htmlCount ".message" 1+ htmlAllContain ".message" "Some Content"+ htmlAllContain ".message" "text/plain" -import qualified Handler.HomeSpec+ -- This is a simple example of using a database access in a test. The+ -- test will succeed for a fresh scaffolded site with an empty database,+ -- but will fail on an existing database with a non-empty user table.+ it "leaves the user table empty" $ do+ get HomeR+ statusIs 200+ users <- runDB $ selectList ([] :: [Filter User]) []+ assertEqual "user table empty" 0 $ length users -main :: IO ()-main = do- conf <- Yesod.Default.Config.loadConfig $ (configSettings Testing)- { csParseExtra = parseExtra- }- foundation <- makeFoundation conf- hspec $ do- yesodSpec foundation $ do- Handler.HomeSpec.spec+{-# START_FILE test/Spec.hs #-}+{-# OPTIONS_GHC -F -pgmF hspec-discover #-} {-# START_FILE test/TestImport.hs #-} module TestImport- ( module Yesod.Test- , module Model- , module Foundation- , module Database.Persist- , module Prelude- , runDB- , Spec- , Example+ ( module TestImport+ , module X ) where -import Yesod.Test-import Database.Persist hiding (get)-import Database.Persist.Sql (SqlPersistM, runSqlPersistMPool)-import Control.Monad.IO.Class (liftIO)-import Prelude--import Foundation-import Model--type Spec = YesodSpec App-type Example = YesodExample App+import Application (makeFoundation)+import ClassyPrelude as X+import Database.Persist as X hiding (get)+import Database.Persist.Sql (SqlPersistM, runSqlPersistMPool)+import Foundation as X+import Model as X+import Test.Hspec as X+import Yesod.Default.Config2 (ignoreEnv, loadAppSettings)+import Yesod.Test as X -runDB :: SqlPersistM a -> Example a+runDB :: SqlPersistM a -> YesodExample App a runDB query = do- pool <- fmap connPool getTestYesod+ pool <- fmap appConnPool getTestYesod liftIO $ runSqlPersistMPool query pool++withApp :: SpecWith App -> Spec+withApp = before $ do+ settings <- loadAppSettings+ ["config/test-settings.yml", "config/settings.yml"]+ []+ ignoreEnv+ makeFoundation settings
hsfiles/simple.hsfiles view
@@ -28,27 +28,29 @@ {-# START_FILE Application.hs #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Application- ( makeApplication- , getApplicationDev+ ( getApplicationDev+ , appMain+ , develMain , makeFoundation ) where +import Control.Monad.Logger (liftLoc) import Import-import Yesod.Default.Config-import Yesod.Default.Main-import Yesod.Default.Handlers-import Network.Wai.Middleware.RequestLogger- ( mkRequestLogger, outputFormat, OutputFormat (..), IPAddrSource (..), destination- )-import qualified Network.Wai.Middleware.RequestLogger as RequestLogger-import Network.HTTP.Client.Conduit (newManager)-import System.Log.FastLogger (newStdoutLoggerSet, defaultBufSize)-import Network.Wai.Logger (clockDateCacher)-import Data.Default (def)-import Yesod.Core.Types (loggerSet, Logger (Logger))+import Language.Haskell.TH.Syntax (qLocation)+import Network.Wai.Handler.Warp (Settings, defaultSettings,+ defaultShouldDisplayException,+ runSettings, setHost,+ setOnException, setPort)+import Network.Wai.Middleware.RequestLogger (Destination (Logger),+ IPAddrSource (..),+ OutputFormat (..), destination,+ mkRequestLogger, outputFormat)+import System.Log.FastLogger (defaultBufSize, newStdoutLoggerSet,+ toLogStr) -- Import all relevant handler modules here. -- Don't forget to add new modules to your cabal file!+import Handler.Common import Handler.Home -- This line actually creates our YesodDispatch instance. It is the second half@@ -56,90 +58,114 @@ -- comments there for more details. mkYesodDispatch "App" resourcesApp --- This function allocates resources (such as a database connection pool),--- performs initialization and creates a WAI application. This is also the--- place to put your migrate statements to have automatic database+-- | This function allocates resources (such as a database connection pool),+-- performs initialization and return a foundation datatype value. This is also+-- the place to put your migrate statements to have automatic database -- migrations handled by Yesod.-makeApplication :: AppConfig DefaultEnv Extra -> IO (Application, LogFunc)-makeApplication conf = do- foundation <- makeFoundation conf+makeFoundation :: AppSettings -> IO App+makeFoundation appSettings = do+ -- Some basic initializations: HTTP connection manager, logger, and static+ -- subsite.+ appHttpManager <- newManager+ appLogger <- newStdoutLoggerSet defaultBufSize >>= makeYesodLogger+ appStatic <-+ (if appMutableStatic appSettings then staticDevel else static)+ (appStaticDir appSettings) - -- Initialize the logging middleware+ -- Return the foundation+ return App {..}++-- | Convert our foundation to a WAI Application by calling @toWaiAppPlain@ and+-- applyng some additional middlewares.+makeApplication :: App -> IO Application+makeApplication foundation = do logWare <- mkRequestLogger def { outputFormat =- if development+ if appDetailedRequestLogging $ appSettings foundation then Detailed True- else Apache FromSocket- , destination = RequestLogger.Logger $ loggerSet $ appLogger foundation+ else Apache+ (if appIpFromHeader $ appSettings foundation+ then FromFallback+ else FromSocket)+ , destination = Logger $ loggerSet $ appLogger foundation } -- Create the WAI application and apply middlewares- app <- toWaiAppPlain foundation- let logFunc = messageLoggerSource foundation (appLogger foundation)- return (logWare $ defaultMiddlewaresNoLogging app, logFunc)+ appPlain <- toWaiAppPlain foundation+ return $ logWare $ defaultMiddlewaresNoLogging appPlain --- | Loads up any necessary settings, creates your foundation datatype, and--- performs some initialization.-makeFoundation :: AppConfig DefaultEnv Extra -> IO App-makeFoundation conf = do- manager <- newManager- s <- staticSite+-- | Warp settings for the given foundation value.+warpSettings :: App -> Settings+warpSettings foundation =+ setPort (appPort $ appSettings foundation)+ $ setHost (appHost $ appSettings foundation)+ $ setOnException (\_req e ->+ when (defaultShouldDisplayException e) $ messageLoggerSource+ foundation+ (appLogger foundation)+ $(qLocation >>= liftLoc)+ "yesod"+ LevelError+ (toLogStr $ "Exception from Warp: " ++ show e))+ defaultSettings - loggerSet' <- newStdoutLoggerSet defaultBufSize- (getter, _) <- clockDateCacher+-- | For yesod devel, return the Warp settings and WAI Application.+getApplicationDev :: IO (Settings, Application)+getApplicationDev = do+ settings <- loadAppSettings [configSettingsYml] [] useEnv+ foundation <- makeFoundation settings+ app <- makeApplication foundation+ wsettings <- getDevSettings $ warpSettings foundation+ return (wsettings, app) - let logger = Yesod.Core.Types.Logger loggerSet' getter- foundation = App- { settings = conf- , getStatic = s- , httpManager = manager- , appLogger = logger- }+-- | main function for use by yesod devel+develMain :: IO ()+develMain = develMainHelper getApplicationDev - return foundation+-- | The @main@ function for an executable running this site.+appMain :: IO ()+appMain = do+ -- Get the settings from all relevant sources+ settings <- loadAppSettingsArgs+ -- fall back to compile-time values, set to [] to require values at runtime+ [configSettingsYmlValue] --- for yesod devel-getApplicationDev :: IO (Int, Application)-getApplicationDev =- defaultDevelApp loader (fmap fst . makeApplication)- where- loader = Yesod.Default.Config.loadConfig (configSettings Development)- { csParseExtra = parseExtra- }+ -- allow environment variables to override+ useEnv + -- Generate the foundation from the settings+ foundation <- makeFoundation settings++ -- Generate a WAI Application from the foundation+ app <- makeApplication foundation++ -- Run the application with Warp+ runSettings (warpSettings foundation) app+ {-# START_FILE Foundation.hs #-} module Foundation where -import Prelude-import Yesod-import Yesod.Static-import Yesod.Default.Config-import Yesod.Default.Util (addStaticContentExternal)-import Network.HTTP.Client.Conduit (Manager, HasHttpManager (getHttpManager))-import qualified Settings-import Settings.Development (development)+import ClassyPrelude.Yesod+import Settings import Settings.StaticFiles-import Settings (widgetFile, Extra (..))-import Text.Jasmine (minifym)-import Text.Hamlet (hamletFile)-import Yesod.Core.Types (Logger)+import Text.Hamlet (hamletFile)+import Text.Jasmine (minifym)+import Yesod.Core.Types (Logger)+import Yesod.Default.Util (addStaticContentExternal) --- | The site argument for your application. This can be a good place to+-- | The foundation datatype for your application. This can be a good place to -- keep settings and values requiring initialization before your application -- starts running, such as database connections. Every handler will have -- access to the data present here. data App = App- { settings :: AppConfig DefaultEnv Extra- , getStatic :: Static -- ^ Settings for static file serving.- , httpManager :: Manager- , appLogger :: Logger+ { appSettings :: AppSettings+ , appStatic :: Static -- ^ Settings for static file serving.+ , appHttpManager :: Manager+ , appLogger :: Logger } instance HasHttpManager App where- getHttpManager = httpManager---- Set up i18n messages. See the message folder.-mkMessage "App" "messages" "en"+ getHttpManager = appHttpManager -- This is where we define all of the routes in our application. For a full -- explanation of the syntax, please see:@@ -150,12 +176,15 @@ -- explanation for this split. mkYesodData "App" $(parseRoutesFile "config/routes") +-- | A convenient synonym for creating forms. type Form x = Html -> MForm (HandlerT App IO) (FormResult x, Widget) -- Please see the documentation for the Yesod typeclass. There are a number -- of settings which can be configured by overriding methods here. instance Yesod App where- approot = ApprootMaster $ appRoot . settings+ -- Controls the base of generated URLs. For more information on modifying,+ -- see: https://github.com/yesodweb/yesod/wiki/Overriding-approot+ approot = ApprootMaster $ appRoot . appSettings -- Store session data on the client in encrypted cookies, -- default session idle timeout is 120 minutes@@ -178,12 +207,6 @@ $(widgetFile "default-layout") withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet") - -- This is done to provide an optimization for serving static files from- -- a separate domain. Please see the staticRoot setting in Settings.hs- urlRenderOverride y (StaticR s) =- Just $ uncurry (joinPath y (Settings.staticRoot $ settings y)) $ renderRoute s- urlRenderOverride _ _ = Nothing- -- Routes not requiring authenitcation. isAuthorized FaviconR _ = return Authorized isAuthorized RobotsR _ = return Authorized@@ -194,21 +217,27 @@ -- 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 =- addStaticContentExternal minifym genFileName Settings.staticDir (StaticR . flip StaticRoute [])+ addStaticContent ext mime content = do+ master <- getYesod+ let staticDir = appStaticDir $ appSettings master+ addStaticContentExternal+ minifym+ genFileName+ staticDir+ (StaticR . flip StaticRoute [])+ ext+ mime+ content where -- Generate a unique filename based on the content itself- genFileName lbs- | development = "autogen-" ++ base64md5 lbs- | otherwise = base64md5 lbs-- -- Place Javascript at bottom of the body tag so the rest of the page loads first- jsLoader _ = BottomOfBody+ genFileName lbs = "autogen-" ++ base64md5 lbs -- What messages should be logged. The following includes all messages when -- in development, and warnings and errors in production.- shouldLog _ _source level =- development || level == LevelWarn || level == LevelError+ shouldLog app _source level =+ appShouldLogAll (appSettings app)+ || level == LevelWarn+ || level == LevelError makeLogger = return . appLogger @@ -217,23 +246,38 @@ instance RenderMessage App FormMessage where renderMessage _ _ = defaultFormMessage --- | Get the 'Extra' value, used to hold data from the settings.yml file.-getExtra :: Handler Extra-getExtra = fmap (appExtra . settings) getYesod---- Note: previous versions of the scaffolding included a deliver function to--- send emails. Unfortunately, there are too many different options for us to--- give a reasonable default. Instead, the information is available on the--- wiki:+-- Note: Some functionality previously present in the scaffolding has been+-- moved to documentation in the Wiki. Following are some hopefully helpful+-- links: -- -- https://github.com/yesodweb/yesod/wiki/Sending-email+-- https://github.com/yesodweb/yesod/wiki/Serve-static-files-from-a-separate-domain+-- https://github.com/yesodweb/yesod/wiki/i18n-messages-in-the-scaffolding +{-# START_FILE Handler/Common.hs #-}+-- | Common handler functions.+module Handler.Common where++import Data.FileEmbed (embedFile)+import Import++-- These handlers embed files in the executable at compile time to avoid a+-- runtime dependency, and for efficiency.++getFaviconR :: Handler TypedContent+getFaviconR = return $ TypedContent "image/x-icon"+ $ toContent $(embedFile "config/favicon.ico")++getRobotsR :: Handler TypedContent+getRobotsR = return $ TypedContent typePlain+ $ toContent $(embedFile "config/robots.txt")+ {-# START_FILE Handler/Home.hs #-} module Handler.Home where import Import-import Yesod.Form.Bootstrap3- ( BootstrapFormLayout (..), renderBootstrap3, withSmallInput )+import Yesod.Form.Bootstrap3 (BootstrapFormLayout (..), renderBootstrap3,+ withSmallInput) -- This is a handler function for the GET request method on the HomeR -- resource pattern. All of your resource patterns are defined in@@ -275,30 +319,12 @@ ( module Import ) where -import Prelude as Import hiding (head, init, last,- readFile, tail, writeFile)-import Yesod as Import hiding (Route (..))--import Control.Applicative as Import (pure, (<$>), (<*>))-import Data.Text as Import (Text)--import Foundation as Import-import Settings as Import-import Settings.Development as Import-import Settings.StaticFiles as Import--#if __GLASGOW_HASKELL__ >= 704-import Data.Monoid as Import- (Monoid (mappend, mempty, mconcat),- (<>))-#else-import Data.Monoid as Import- (Monoid (mappend, mempty, mconcat))--infixr 5 <>-(<>) :: Monoid m => m -> m -> m-(<>) = mappend-#endif+import ClassyPrelude.Yesod as Import+import Foundation as Import+import Settings as Import+import Settings.StaticFiles as Import+import Yesod.Core.Types as Import (loggerSet)+import Yesod.Default.Config2 as Import {-# START_FILE PROJECTNAME.cabal #-} name: PROJECTNAME@@ -320,7 +346,7 @@ Import Settings Settings.StaticFiles- Settings.Development+ Handler.Common Handler.Home if flag(dev) || flag(library-only)@@ -344,13 +370,17 @@ DeriveDataTypeable ViewPatterns TupleSections+ RecordWildCards build-depends: base >= 4 && < 5- , yesod >= 1.4.0 && < 1.5+ , yesod >= 1.4.1 && < 1.5 , yesod-core >= 1.4.0 && < 1.5 , yesod-auth >= 1.4.0 && < 1.5- , yesod-static >= 1.4.0 && < 1.5+ , yesod-static >= 1.4.0.3 && < 1.5 , yesod-form >= 1.4.0 && < 1.5+ , classy-prelude >= 0.10.2+ , classy-prelude-conduit >= 0.10.2+ , classy-prelude-yesod >= 0.10.2 , bytestring >= 0.9 && < 0.11 , text >= 0.11 && < 2.0 , template-haskell@@ -368,10 +398,12 @@ , monad-logger >= 0.3 && < 0.4 , fast-logger >= 2.2 && < 2.3 , wai-logger >= 2.2 && < 2.3-- -- see https://github.com/yesodweb/yesod/issues/814- if !os(windows)- build-depends: unix+ , file-embed+ , safe+ , unordered-containers+ , containers+ , vector+ , time executable PROJECTNAME if flag(library-only)@@ -379,9 +411,7 @@ main-is: main.hs hs-source-dirs: app- build-depends: base- , PROJECTNAME- , yesod+ build-depends: base, PROJECTNAME ghc-options: -threaded -O2 -rtsopts -with-rtsopts=-N @@ -409,10 +439,12 @@ build-depends: base , PROJECTNAME- , yesod-test >= 1.4 && < 1.5+ , yesod-test >= 1.4.2 && < 1.5 , yesod-core , yesod , hspec+ , classy-prelude+ , classy-prelude-yesod {-# START_FILE Settings.hs #-} -- | Settings are centralized, as much as possible, into this file. This@@ -422,41 +454,77 @@ -- declared in the Foundation.hs file. module Settings where -import Prelude-import Text.Shakespeare.Text (st)-import Language.Haskell.TH.Syntax-import Yesod.Default.Config-import Yesod.Default.Util-import Data.Text (Text)-import Data.Yaml-import Control.Applicative-import Settings.Development-import Data.Default (def)-import Text.Hamlet+import ClassyPrelude.Yesod+import Control.Exception (throw)+import Data.Aeson (Result (..), fromJSON, withObject, (.!=),+ (.:?))+import Data.FileEmbed (embedFile)+import Data.Yaml (decodeEither')+import Language.Haskell.TH.Syntax (Exp, Name, Q)+import Network.Wai.Handler.Warp (HostPreference)+import Yesod.Default.Config2 (applyEnvValue, configSettingsYml)+import Yesod.Default.Util (WidgetFileSettings, widgetFileNoReload,+ widgetFileReload) --- Static setting below. Changing these requires a recompile+-- | Runtime settings to configure this application. These settings can be+-- loaded from various sources: defaults, environment variables, config files,+-- theoretically even a database.+data AppSettings = AppSettings+ { appStaticDir :: String+ -- ^ Directory from which to serve static files.+ , appRoot :: Text+ -- ^ Base for all generated URLs.+ , appHost :: HostPreference+ -- ^ Host/interface the server should bind to.+ , appPort :: Int+ -- ^ Port to listen on+ , appIpFromHeader :: Bool+ -- ^ Get the IP address from the header when logging. Useful when sitting+ -- behind a reverse proxy. --- | 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-staticDir = "static"+ , appDetailedRequestLogging :: Bool+ -- ^ Use detailed request logging system+ , appShouldLogAll :: Bool+ -- ^ Should all log messages be displayed?+ , appReloadTemplates :: Bool+ -- ^ Use the reload version of templates+ , appMutableStatic :: Bool+ -- ^ Assume that files in the static dir may change after compilation+ , appSkipCombining :: Bool+ -- ^ Perform no stylesheet/script combining --- | The base URL for your static files. As you can see by the default--- value, this can simply be "static" appended to your application root.--- A powerful optimization can be serving static files from a separate--- domain name. This allows you to use a web server optimized for static--- files, more easily set expires and cache values, and avoid possibly--- costly transference of cookies on static files. For more information,--- please see:--- http://code.google.com/speed/page-speed/docs/request.html#ServeFromCookielessDomain------ If you change the resource pattern for StaticR in Foundation.hs, you will--- have to make a corresponding change here.------ To see how this value is used, see urlRenderOverride in Foundation.hs-staticRoot :: AppConfig DefaultEnv x -> Text-staticRoot conf = [st|#{appRoot conf}/static|]+ -- Example app-specific configuration values.+ , appCopyright :: Text+ -- ^ Copyright text to appear in the footer of the page+ , appAnalytics :: Maybe Text+ -- ^ Google Analytics code+ } +instance FromJSON AppSettings where+ parseJSON = withObject "AppSettings" $ \o -> do+ let defaultDev =+#if DEVELOPMENT+ True+#else+ False+#endif+ appStaticDir <- o .: "static-dir"+ appRoot <- o .: "approot"+ appHost <- fromString <$> o .: "host"+ appPort <- o .: "port"+ appIpFromHeader <- o .: "ip-from-header"++ appDetailedRequestLogging <- o .:? "detailed-logging" .!= defaultDev+ appShouldLogAll <- o .:? "should-log-all" .!= defaultDev+ appReloadTemplates <- o .:? "reload-templates" .!= defaultDev+ appMutableStatic <- o .:? "mutable-static" .!= defaultDev+ appSkipCombining <- o .:? "skip-combining" .!= defaultDev++ appCopyright <- o .: "copyright"+ appAnalytics <- o .:? "analytics"++ return AppSettings {..}+ -- | Settings for 'widgetFile', such as which template languages to support and -- default Hamlet settings. --@@ -465,81 +533,62 @@ -- https://github.com/yesodweb/yesod/wiki/Overriding-widgetFile widgetFileSettings :: WidgetFileSettings widgetFileSettings = def- { wfsHamletSettings = defaultHamletSettings- { hamletNewlines = AlwaysNewlines- }- } +-- | How static files should be combined.+combineSettings :: CombineSettings+combineSettings = def+ -- The rest of this file contains settings which rarely need changing by a -- user. widgetFile :: String -> Q Exp-widgetFile = (if development then widgetFileReload- else widgetFileNoReload)+widgetFile = (if appReloadTemplates compileTimeAppSettings+ then widgetFileReload+ else widgetFileNoReload) widgetFileSettings -data Extra = Extra- { extraCopyright :: Text- , extraAnalytics :: Maybe Text -- ^ Google Analytics- } deriving Show+-- | Raw bytes at compile time of @config/settings.yml@+configSettingsYmlBS :: ByteString+configSettingsYmlBS = $(embedFile configSettingsYml) -parseExtra :: DefaultEnv -> Object -> Parser Extra-parseExtra _ o = Extra- <$> o .: "copyright"- <*> o .:? "analytics"+-- | @config/settings.yml@, parsed to a @Value@.+configSettingsYmlValue :: Value+configSettingsYmlValue = either throw id $ decodeEither' configSettingsYmlBS -{-# START_FILE Settings/Development.hs #-}-module Settings.Development where+-- | A version of @AppSettings@ parsed at compile time from @config/settings.yml@.+compileTimeAppSettings :: AppSettings+compileTimeAppSettings =+ case fromJSON $ applyEnvValue False mempty configSettingsYmlValue of+ Error e -> error e+ Success settings -> settings -import Prelude+-- The following two functions can be used to combine multiple CSS or JS files+-- at compile time to decrease the number of http requests.+-- Sample usage (inside a Widget):+--+-- > $(combineStylesheets 'StaticR [style1_css, style2_css]) -development :: Bool-development =-#if DEVELOPMENT- True-#else- False-#endif+combineStylesheets :: Name -> [Route Static] -> Q Exp+combineStylesheets = combineStylesheets'+ (appSkipCombining compileTimeAppSettings)+ combineSettings -production :: Bool-production = not development+combineScripts :: Name -> [Route Static] -> Q Exp+combineScripts = combineScripts'+ (appSkipCombining compileTimeAppSettings)+ combineSettings {-# START_FILE Settings/StaticFiles.hs #-} module Settings.StaticFiles where -import Prelude (IO)-import Yesod.Static-import qualified Yesod.Static as Static-import Settings (staticDir)-import Settings.Development-import Language.Haskell.TH (Q, Exp, Name)-import Data.Default (def)---- | use this to create your static file serving site-staticSite :: IO Static.Static-staticSite = if development then Static.staticDevel staticDir- else Static.static staticDir+import Settings (appStaticDir, compileTimeAppSettings)+import Yesod.Static (staticFiles) -- | This generates easy references to files in the static directory at compile time, -- giving you compile-time verification that referenced files exist. -- Warning: any files added to your static directory during run-time can't be -- accessed this way. You'll have to use their FilePath or URL to access them.-$(staticFiles Settings.staticDir)--combineSettings :: CombineSettings-combineSettings = def---- The following two functions can be used to combine multiple CSS or JS files--- at compile time to decrease the number of http requests.--- Sample usage (inside a Widget):------ > $(combineStylesheets 'StaticR [style1_css, style2_css])--combineStylesheets :: Name -> [Route Static] -> Q Exp-combineStylesheets = combineStylesheets' development combineSettings--combineScripts :: Name -> [Route Static] -> Q Exp-combineScripts = combineScripts' development combineSettings+staticFiles (appStaticDir compileTimeAppSettings) {-# START_FILE app/DevelMain.hs #-} -- | Development version to be run inside GHCi.@@ -606,19 +655,24 @@ start :: MVar () -- ^ Written to when the thread is killed. -> IO ThreadId start done = do- (port,app) <- getApplicationDev- forkIO (finally (runSettings (setPort port defaultSettings) app)+ (settings,app) <- getApplicationDev+ forkIO (finally (runSettings settings app) (putMVar done ())) +{-# START_FILE app/devel.hs #-}+{-# LANGUAGE PackageImports #-}+import "PROJECTNAME" Application (develMain)+import Prelude (IO)++main :: IO ()+main = develMain+ {-# START_FILE app/main.hs #-}-import Prelude (IO)-import Yesod.Default.Config (fromArgs)-import Yesod.Default.Main (defaultMainLog)-import Settings (parseExtra)-import Application (makeApplication)+import Prelude (IO)+import Application (appMain) main :: IO ()-main = defaultMainLog (fromArgs parseExtra) makeApplication+main = appMain {-# START_FILE BASE64 config/favicon.ico #-} AAABAAIAEBAAAAEAIABoBAAAJgAAABAQAgABAAEAsAAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAA@@ -713,7 +767,7 @@ User-agent: * {-# START_FILE config/routes #-}-/static StaticR Static getStatic+/static StaticR Static appStatic /favicon.ico FaviconR GET /robots.txt RobotsR GET@@ -721,155 +775,28 @@ / HomeR GET POST {-# START_FILE config/settings.yml #-}-Default: &defaults- host: "*4" # any IPv4 host- port: 3000- approot: "http://localhost:3000"- copyright: Insert copyright statement here- #analytics: UA-YOURCODE--Development:- <<: *defaults--Testing:- <<: *defaults--Staging:- <<: *defaults--Production:- #approot: "http://www.example.com"- <<: *defaults--{-# START_FILE deploy/Procfile #-}-# Free deployment to Heroku.-#-# !! Warning: You must use a 64 bit machine to compile !!-#-# This could mean using a virtual machine. Give your VM as much memory as you can to speed up linking.-#-# Basic Yesod setup:-#-# * Move this file out of the deploy directory and into your root directory-#-# mv deploy/Procfile ./-#-# * Create an empty package.json-# echo '{ "name": "PROJECTNAME", "version": "0.0.1", "dependencies": {} }' >> package.json-#-# Postgresql Yesod setup:-#-# * add dependencies on the "heroku", "aeson" and "unordered-containers" packages in your cabal file-#-# * add code in Application.hs to use the heroku package and load the connection parameters.-# The below works for Postgresql.-#-# import Data.HashMap.Strict as H-# import Data.Aeson.Types as AT-# #ifndef DEVELOPMENT-# import qualified Web.Heroku-# #endif-#-#-#-# makeFoundation :: AppConfig DefaultEnv Extra -> Logger -> IO App-# makeFoundation conf setLogger = do-# manager <- newManager def-# s <- staticSite-# hconfig <- loadHerokuConfig-# dbconf <- withYamlEnvironment "config/postgresql.yml" (appEnv conf)-# (Database.Persist.Store.loadConfig . combineMappings hconfig) >>=-# Database.Persist.Store.applyEnv-# p <- Database.Persist.Store.createPoolConfig (dbconf :: Settings.PersistConfig)-# Database.Persist.Store.runPool dbconf (runMigration migrateAll) p-# return $ App conf setLogger s p manager dbconf-#-# #ifndef DEVELOPMENT-# canonicalizeKey :: (Text, val) -> (Text, val)-# canonicalizeKey ("dbname", val) = ("database", val)-# canonicalizeKey pair = pair-#-# toMapping :: [(Text, Text)] -> AT.Value-# toMapping xs = AT.Object $ M.fromList $ map (\(key, val) -> (key, AT.String val)) xs-# #endif-#-# combineMappings :: AT.Value -> AT.Value -> AT.Value-# combineMappings (AT.Object m1) (AT.Object m2) = AT.Object $ m1 `M.union` m2-# combineMappings _ _ = error "Data.Object is not a Mapping."-#-# loadHerokuConfig :: IO AT.Value-# loadHerokuConfig = do-# #ifdef DEVELOPMENT-# return $ AT.Object M.empty-# #else-# Web.Heroku.dbConnParams >>= return . toMapping . map canonicalizeKey-# #endif--+static-dir: "_env:STATIC_DIR:static"+host: "_env:HOST:*4" # any IPv4 host+port: "_env:PORT:3000"+approot: "_env:APPROOT:http://localhost:3000"+ip-from-header: "_env:IP_FROM_HEADER:false" -# Heroku setup:-# Find the Heroku guide. Roughly:-#-# * sign up for a heroku account and register your ssh key-# * create a new application on the *cedar* stack-#-# * make your Yesod project the git repository for that application-# * create a deploy branch-#-# git checkout -b deploy-#-# Repeat these steps to deploy:-# * add your web executable binary (referenced below) to the git repository-#-# git checkout deploy-# git add ./dist/build/PROJECTNAME/PROJECTNAME-# git commit -m deploy-#-# * push to Heroku+# Optional values with the following production defaults.+# In development, they default to the inverse. #-# git push heroku deploy:master---# Heroku configuration that runs your app-web: ./dist/build/PROJECTNAME/PROJECTNAME production -p $PORT--{-# START_FILE devel.hs #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE PackageImports #-}-import "PROJECTNAME" Application (getApplicationDev)-import Network.Wai.Handler.Warp- (runSettings, defaultSettings, setPort)-import Control.Concurrent (forkIO)-import System.Directory (doesFileExist, removeFile)-import System.Exit (exitSuccess)-import Control.Concurrent (threadDelay)--#ifndef mingw32_HOST_OS-import System.Posix.Signals (installHandler, sigINT, Handler(Catch))-#endif--main :: IO ()-main = do-#ifndef mingw32_HOST_OS- _ <- installHandler sigINT (Catch $ return ()) Nothing-#endif-- putStrLn "Starting devel application"- (port, app) <- getApplicationDev- forkIO $ runSettings (setPort port defaultSettings) app- loop--loop :: IO ()-loop = do- threadDelay 100000- e <- doesFileExist "yesod-devel/devel-terminate"- if e then terminateDevel else loop+# development: false+# detailed-logging: false+# should-log-all: false+# reload-templates: false+# mutable-static: false+# skip-combining: false -terminateDevel :: IO ()-terminateDevel = exitSuccess+copyright: Insert copyright statement here+#analytics: UA-YOURCODE -{-# START_FILE messages/en.msg #-}-Hello: Hello+{-# START_FILE config/test-settings.yml #-}+database:+ database: PROJECTNAME_test {-# START_FILE static/css/bootstrap.css #-} /*!@@ -8835,9 +8762,9 @@ <div id="main" role="main"> ^{pageBody pc} <footer>- #{extraCopyright $ appExtra $ settings master}+ #{appCopyright $ appSettings master} - $maybe analytics <- extraAnalytics $ appExtra $ settings master+ $maybe analytics <- appAnalytics $ appSettings master <script> if(!window.location.href.match(/localhost/)){ window._gaq = [['_setAccount','#{analytics}'],['_trackPageview'],['_trackPageLoadTime']];@@ -8860,7 +8787,7 @@ ^{widget} {-# START_FILE templates/homepage.hamlet #-}-<h1>_{MsgHello}+<h1>Welcome to Yesod! <ol> <li>Now that you have a working project you should use the #@@ -8911,69 +8838,71 @@ color: #990 } -{-# START_FILE test/Handler/HomeSpec.hs #-}-module Handler.HomeSpec- ( spec- ) where+{-# START_FILE test/Handler/CommonSpec.hs #-}+module Handler.CommonSpec (spec) where import TestImport spec :: Spec-spec =- ydescribe "These are some example tests" $ do-- yit "loads the index and checks it looks right" $ do- get HomeR+spec = withApp $ do+ describe "robots.txt" $ do+ it "gives a 200" $ do+ get RobotsR statusIs 200- htmlAllContain "h1" "Hello"+ it "has correct User-agent" $ do+ get RobotsR+ bodyContains "User-agent: *"+ describe "favicon.ico" $ do+ it "gives a 200" $ do+ get FaviconR+ statusIs 200 - request $ do- setMethod "POST"- setUrl HomeR- addNonce- fileByLabel "Choose a file" "test/Spec.hs" "text/plain" -- talk about self-reference- byLabel "What's on the file?" "Some Content"+{-# START_FILE test/Handler/HomeSpec.hs #-}+module Handler.HomeSpec (spec) where - statusIs 200- -- more debugging printBody- htmlCount ".message" 1- htmlAllContain ".message" "Some Content"- htmlAllContain ".message" "text/plain"+import TestImport -{-# START_FILE test/Spec.hs #-}-module Main where+spec :: Spec+spec = withApp $ do+ it "loads the index and checks it looks right" $ do+ get HomeR+ statusIs 200+ htmlAllContain "h1" "Welcome to Yesod" -import Import-import Yesod.Default.Config-import Yesod.Test-import Test.Hspec (hspec)-import Application (makeFoundation)+ request $ do+ setMethod "POST"+ setUrl HomeR+ addNonce+ fileByLabel "Choose a file" "test/Spec.hs" "text/plain" -- talk about self-reference+ byLabel "What's on the file?" "Some Content" -import qualified Handler.HomeSpec+ statusIs 200+ -- more debugging printBody+ htmlCount ".message" 1+ htmlAllContain ".message" "Some Content"+ htmlAllContain ".message" "text/plain" -main :: IO ()-main = do- conf <- Yesod.Default.Config.loadConfig $ (configSettings Testing)- { csParseExtra = parseExtra- }- foundation <- makeFoundation conf- hspec $ do- yesodSpec foundation $ do- Handler.HomeSpec.spec+{-# START_FILE test/Spec.hs #-}+{-# OPTIONS_GHC -F -pgmF hspec-discover #-} {-# START_FILE test/TestImport.hs #-} module TestImport- ( module Yesod.Test- , module Foundation- , module Prelude- , Spec- , Example+ ( module TestImport+ , module X ) where -import Yesod.Test-import Prelude-import Foundation+import Application (makeFoundation)+import ClassyPrelude as X+import Foundation as X+import Test.Hspec as X+import Yesod.Default.Config2 (ignoreEnv, loadAppSettings)+import Yesod.Test as X -type Spec = YesodSpec App-type Example = YesodExample App+withApp :: SpecWith App -> Spec+withApp = before $ do+ settings <- loadAppSettings+ ["config/test-settings.yml", "config/settings.yml"]+ []+ ignoreEnv+ makeFoundation settings
hsfiles/sqlite.hsfiles view
@@ -28,32 +28,31 @@ {-# START_FILE Application.hs #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Application- ( makeApplication- , getApplicationDev+ ( getApplicationDev+ , appMain+ , develMain , makeFoundation ) where +import Control.Monad.Logger (liftLoc, runLoggingT)+import Database.Persist.Sqlite (createSqlitePool, runSqlPool,+ sqlDatabase, sqlPoolSize) import Import-import Yesod.Auth-import Yesod.Default.Config-import Yesod.Default.Main-import Yesod.Default.Handlers-import Network.Wai.Middleware.RequestLogger- ( mkRequestLogger, outputFormat, OutputFormat (..), IPAddrSource (..), destination- )-import qualified Network.Wai.Middleware.RequestLogger as RequestLogger-import qualified Database.Persist-import Database.Persist.Sql (runMigration)-import Database.Persist.Sqlite (createSqlitePool, sqlDatabase, sqlPoolSize)-import Network.HTTP.Client.Conduit (newManager)-import Control.Monad.Logger (runLoggingT)-import System.Log.FastLogger (newStdoutLoggerSet, defaultBufSize)-import Network.Wai.Logger (clockDateCacher)-import Data.Default (def)-import Yesod.Core.Types (loggerSet, Logger (Logger))+import Language.Haskell.TH.Syntax (qLocation)+import Network.Wai.Handler.Warp (Settings, defaultSettings,+ defaultShouldDisplayException,+ runSettings, setHost,+ setOnException, setPort)+import Network.Wai.Middleware.RequestLogger (Destination (Logger),+ IPAddrSource (..),+ OutputFormat (..), destination,+ mkRequestLogger, outputFormat)+import System.Log.FastLogger (defaultBufSize, newStdoutLoggerSet,+ toLogStr) -- Import all relevant handler modules here. -- Don't forget to add new modules to your cabal file!+import Handler.Common import Handler.Home -- This line actually creates our YesodDispatch instance. It is the second half@@ -61,112 +60,136 @@ -- comments there for more details. mkYesodDispatch "App" resourcesApp --- This function allocates resources (such as a database connection pool),--- performs initialization and creates a WAI application. This is also the--- place to put your migrate statements to have automatic database+-- | This function allocates resources (such as a database connection pool),+-- performs initialization and return a foundation datatype value. This is also+-- the place to put your migrate statements to have automatic database -- migrations handled by Yesod.-makeApplication :: AppConfig DefaultEnv Extra -> IO (Application, LogFunc)-makeApplication conf = do- foundation <- makeFoundation conf+makeFoundation :: AppSettings -> IO App+makeFoundation appSettings = do+ -- Some basic initializations: HTTP connection manager, logger, and static+ -- subsite.+ appHttpManager <- newManager+ appLogger <- newStdoutLoggerSet defaultBufSize >>= makeYesodLogger+ appStatic <-+ (if appMutableStatic appSettings then staticDevel else static)+ (appStaticDir appSettings) - -- Initialize the logging middleware+ -- We need a log function to create a connection pool. We need a connection+ -- pool to create our foundation. And we need our foundation to get a+ -- logging function. To get out of this loop, we initially create a+ -- temporary foundation without a real connection pool, get a log function+ -- from there, and then create the real foundation.+ let mkFoundation appConnPool = App {..}+ tempFoundation = mkFoundation $ error "connPool forced in tempFoundation"+ logFunc = messageLoggerSource tempFoundation appLogger++ -- Create the database connection pool+ pool <- flip runLoggingT logFunc $ createSqlitePool+ (sqlDatabase $ appDatabaseConf appSettings)+ (sqlPoolSize $ appDatabaseConf appSettings)++ -- Perform database migration using our application's logging settings.+ runLoggingT (runSqlPool (runMigration migrateAll) pool) logFunc++ -- Return the foundation+ return $ mkFoundation pool++-- | Convert our foundation to a WAI Application by calling @toWaiAppPlain@ and+-- applyng some additional middlewares.+makeApplication :: App -> IO Application+makeApplication foundation = do logWare <- mkRequestLogger def { outputFormat =- if development+ if appDetailedRequestLogging $ appSettings foundation then Detailed True- else Apache FromSocket- , destination = RequestLogger.Logger $ loggerSet $ appLogger foundation+ else Apache+ (if appIpFromHeader $ appSettings foundation+ then FromFallback+ else FromSocket)+ , destination = Logger $ loggerSet $ appLogger foundation } -- Create the WAI application and apply middlewares- app <- toWaiAppPlain foundation- let logFunc = messageLoggerSource foundation (appLogger foundation)- return (logWare $ defaultMiddlewaresNoLogging app, logFunc)+ appPlain <- toWaiAppPlain foundation+ return $ logWare $ defaultMiddlewaresNoLogging appPlain --- | Loads up any necessary settings, creates your foundation datatype, and--- performs some initialization.-makeFoundation :: AppConfig DefaultEnv Extra -> IO App-makeFoundation conf = do- manager <- newManager- s <- staticSite- dbconf <- withYamlEnvironment "config/sqlite.yml" (appEnv conf)- Database.Persist.loadConfig >>=- Database.Persist.applyEnv+-- | Warp settings for the given foundation value.+warpSettings :: App -> Settings+warpSettings foundation =+ setPort (appPort $ appSettings foundation)+ $ setHost (appHost $ appSettings foundation)+ $ setOnException (\_req e ->+ when (defaultShouldDisplayException e) $ messageLoggerSource+ foundation+ (appLogger foundation)+ $(qLocation >>= liftLoc)+ "yesod"+ LevelError+ (toLogStr $ "Exception from Warp: " ++ show e))+ defaultSettings - loggerSet' <- newStdoutLoggerSet defaultBufSize- (getter, _) <- clockDateCacher+-- | For yesod devel, return the Warp settings and WAI Application.+getApplicationDev :: IO (Settings, Application)+getApplicationDev = do+ settings <- loadAppSettings [configSettingsYml] [] useEnv+ foundation <- makeFoundation settings+ app <- makeApplication foundation+ wsettings <- getDevSettings $ warpSettings foundation+ return (wsettings, app) - let logger = Yesod.Core.Types.Logger loggerSet' getter- mkFoundation p = App- { settings = conf- , getStatic = s- , connPool = p- , httpManager = manager- , persistConfig = dbconf- , appLogger = logger- }- tempFoundation = mkFoundation $ error "connPool forced in tempFoundation"- logFunc = messageLoggerSource tempFoundation logger+-- | main function for use by yesod devel+develMain :: IO ()+develMain = develMainHelper getApplicationDev - p <- flip runLoggingT logFunc- $ createSqlitePool (sqlDatabase dbconf) (sqlPoolSize dbconf)- let foundation = mkFoundation p+-- | The @main@ function for an executable running this site.+appMain :: IO ()+appMain = do+ -- Get the settings from all relevant sources+ settings <- loadAppSettingsArgs+ -- fall back to compile-time values, set to [] to require values at runtime+ [configSettingsYmlValue] - -- Perform database migration using our application's logging settings.- flip runLoggingT logFunc- (Database.Persist.runPool dbconf (runMigration migrateAll) p)+ -- allow environment variables to override+ useEnv - return foundation+ -- Generate the foundation from the settings+ foundation <- makeFoundation settings --- for yesod devel-getApplicationDev :: IO (Int, Application)-getApplicationDev =- defaultDevelApp loader (fmap fst . makeApplication)- where- loader = Yesod.Default.Config.loadConfig (configSettings Development)- { csParseExtra = parseExtra- }+ -- Generate a WAI Application from the foundation+ app <- makeApplication foundation + -- Run the application with Warp+ runSettings (warpSettings foundation) app+ {-# START_FILE Foundation.hs #-} module Foundation where -import Prelude-import Yesod-import Yesod.Static-import Yesod.Auth-import Yesod.Auth.BrowserId-import Yesod.Default.Config-import Yesod.Default.Util (addStaticContentExternal)-import Network.HTTP.Client.Conduit (Manager, HasHttpManager (getHttpManager))-import qualified Settings-import Settings.Development (development)-import qualified Database.Persist-import Database.Persist.Sql (SqlBackend)-import Settings.StaticFiles-import Settings (widgetFile, Extra (..))+import ClassyPrelude.Yesod+import Database.Persist.Sql (ConnectionPool, runSqlPool) import Model-import Text.Jasmine (minifym)-import Text.Hamlet (hamletFile)-import Yesod.Core.Types (Logger)+import Settings+import Settings.StaticFiles+import Text.Hamlet (hamletFile)+import Text.Jasmine (minifym)+import Yesod.Auth+import Yesod.Auth.BrowserId (authBrowserId)+import Yesod.Core.Types (Logger)+import Yesod.Default.Util (addStaticContentExternal) --- | The site argument for your application. This can be a good place to+-- | The foundation datatype for your application. This can be a good place to -- keep settings and values requiring initialization before your application -- starts running, such as database connections. Every handler will have -- access to the data present here. data App = App- { settings :: AppConfig DefaultEnv Extra- , getStatic :: Static -- ^ Settings for static file serving.- , connPool :: Database.Persist.PersistConfigPool Settings.PersistConf -- ^ Database connection pool.- , httpManager :: Manager- , persistConfig :: Settings.PersistConf- , appLogger :: Logger+ { appSettings :: AppSettings+ , appStatic :: Static -- ^ Settings for static file serving.+ , appConnPool :: ConnectionPool -- ^ Database connection pool.+ , appHttpManager :: Manager+ , appLogger :: Logger } instance HasHttpManager App where- getHttpManager = httpManager---- Set up i18n messages. See the message folder.-mkMessage "App" "messages" "en"+ getHttpManager = appHttpManager -- This is where we define all of the routes in our application. For a full -- explanation of the syntax, please see:@@ -177,12 +200,15 @@ -- explanation for this split. mkYesodData "App" $(parseRoutesFile "config/routes") +-- | A convenient synonym for creating forms. type Form x = Html -> MForm (HandlerT App IO) (FormResult x, Widget) -- Please see the documentation for the Yesod typeclass. There are a number -- of settings which can be configured by overriding methods here. instance Yesod App where- approot = ApprootMaster $ appRoot . settings+ -- Controls the base of generated URLs. For more information on modifying,+ -- see: https://github.com/yesodweb/yesod/wiki/Overriding-approot+ approot = ApprootMaster $ appRoot . appSettings -- Store session data on the client in encrypted cookies, -- default session idle timeout is 120 minutes@@ -205,12 +231,6 @@ $(widgetFile "default-layout") withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet") - -- This is done to provide an optimization for serving static files from- -- a separate domain. Please see the staticRoot setting in Settings.hs- urlRenderOverride y (StaticR s) =- Just $ uncurry (joinPath y (Settings.staticRoot $ settings y)) $ renderRoute s- urlRenderOverride _ _ = Nothing- -- The page to be redirected to when authentication is required. authRoute _ = Just $ AuthR LoginR @@ -225,30 +245,38 @@ -- 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 =- addStaticContentExternal minifym genFileName Settings.staticDir (StaticR . flip StaticRoute [])+ addStaticContent ext mime content = do+ master <- getYesod+ let staticDir = appStaticDir $ appSettings master+ addStaticContentExternal+ minifym+ genFileName+ staticDir+ (StaticR . flip StaticRoute [])+ ext+ mime+ content where -- Generate a unique filename based on the content itself- genFileName lbs- | development = "autogen-" ++ base64md5 lbs- | otherwise = base64md5 lbs-- -- Place Javascript at bottom of the body tag so the rest of the page loads first- jsLoader _ = BottomOfBody+ genFileName lbs = "autogen-" ++ base64md5 lbs -- What messages should be logged. The following includes all messages when -- in development, and warnings and errors in production.- shouldLog _ _source level =- development || level == LevelWarn || level == LevelError+ shouldLog app _source level =+ appShouldLogAll (appSettings app)+ || level == LevelWarn+ || level == LevelError makeLogger = return . appLogger -- How to run database actions. instance YesodPersist App where type YesodPersistBackend App = SqlBackend- runDB = defaultRunDB persistConfig connPool+ runDB action = do+ master <- getYesod+ runSqlPool action $ appConnPool master instance YesodPersistRunner App where- getDBRunner = defaultGetDBRunner connPool+ getDBRunner = defaultGetDBRunner appConnPool instance YesodAuth App where type AuthId App = UserId@@ -257,6 +285,8 @@ loginDest _ = HomeR -- Where to send a user after logout logoutDest _ = HomeR+ -- Override the above two destinations when a Referer: header is present+ redirectToReferer _ = True getAuthId creds = runDB $ do x <- getBy $ UniqueUser $ credsIdent creds@@ -271,7 +301,7 @@ -- You can add other plugins like BrowserID, email or OAuth here authPlugins _ = [authBrowserId def] - authHttpManager = httpManager+ authHttpManager = getHttpManager instance YesodAuthPersist App @@ -280,23 +310,38 @@ instance RenderMessage App FormMessage where renderMessage _ _ = defaultFormMessage --- | Get the 'Extra' value, used to hold data from the settings.yml file.-getExtra :: Handler Extra-getExtra = fmap (appExtra . settings) getYesod---- Note: previous versions of the scaffolding included a deliver function to--- send emails. Unfortunately, there are too many different options for us to--- give a reasonable default. Instead, the information is available on the--- wiki:+-- Note: Some functionality previously present in the scaffolding has been+-- moved to documentation in the Wiki. Following are some hopefully helpful+-- links: -- -- https://github.com/yesodweb/yesod/wiki/Sending-email+-- https://github.com/yesodweb/yesod/wiki/Serve-static-files-from-a-separate-domain+-- https://github.com/yesodweb/yesod/wiki/i18n-messages-in-the-scaffolding +{-# START_FILE Handler/Common.hs #-}+-- | Common handler functions.+module Handler.Common where++import Data.FileEmbed (embedFile)+import Import++-- These handlers embed files in the executable at compile time to avoid a+-- runtime dependency, and for efficiency.++getFaviconR :: Handler TypedContent+getFaviconR = return $ TypedContent "image/x-icon"+ $ toContent $(embedFile "config/favicon.ico")++getRobotsR :: Handler TypedContent+getRobotsR = return $ TypedContent typePlain+ $ toContent $(embedFile "config/robots.txt")+ {-# START_FILE Handler/Home.hs #-} module Handler.Home where import Import-import Yesod.Form.Bootstrap3- ( BootstrapFormLayout (..), renderBootstrap3, withSmallInput )+import Yesod.Form.Bootstrap3 (BootstrapFormLayout (..), renderBootstrap3,+ withSmallInput) -- This is a handler function for the GET request method on the HomeR -- resource pattern. All of your resource patterns are defined in@@ -338,40 +383,20 @@ ( module Import ) where -import Prelude as Import hiding (head, init, last,- readFile, tail, writeFile)-import Yesod as Import hiding (Route (..))--import Control.Applicative as Import (pure, (<$>), (<*>))-import Data.Text as Import (Text)--import Foundation as Import-import Model as Import-import Settings as Import-import Settings.Development as Import-import Settings.StaticFiles as Import--#if __GLASGOW_HASKELL__ >= 704-import Data.Monoid as Import- (Monoid (mappend, mempty, mconcat),- (<>))-#else-import Data.Monoid as Import- (Monoid (mappend, mempty, mconcat))--infixr 5 <>-(<>) :: Monoid m => m -> m -> m-(<>) = mappend-#endif+import ClassyPrelude.Yesod as Import+import Foundation as Import+import Model as Import+import Settings as Import+import Settings.StaticFiles as Import+import Yesod.Auth as Import+import Yesod.Core.Types as Import (loggerSet)+import Yesod.Default.Config2 as Import {-# START_FILE Model.hs #-} module Model where -import Yesod-import Data.Text (Text)+import ClassyPrelude.Yesod import Database.Persist.Quasi-import Data.Typeable (Typeable)-import Prelude -- You can define all of your database entities in the entities file. -- You can find more information on persistent and how to declare entities@@ -401,7 +426,7 @@ Model Settings Settings.StaticFiles- Settings.Development+ Handler.Common Handler.Home if flag(dev) || flag(library-only)@@ -425,17 +450,21 @@ DeriveDataTypeable ViewPatterns TupleSections+ RecordWildCards build-depends: base >= 4 && < 5- , yesod >= 1.4.0 && < 1.5+ , yesod >= 1.4.1 && < 1.5 , yesod-core >= 1.4.0 && < 1.5 , yesod-auth >= 1.4.0 && < 1.5- , yesod-static >= 1.4.0 && < 1.5+ , yesod-static >= 1.4.0.3 && < 1.5 , yesod-form >= 1.4.0 && < 1.5+ , classy-prelude >= 0.10.2+ , classy-prelude-conduit >= 0.10.2+ , classy-prelude-yesod >= 0.10.2 , bytestring >= 0.9 && < 0.11 , text >= 0.11 && < 2.0 , persistent >= 2.0 && < 2.2- , persistent-sqlite >= 2.0 && < 2.2+ , persistent-sqlite >= 2.1.1 && < 2.2 , persistent-template >= 2.0 && < 2.2 , template-haskell , shakespeare >= 2.0 && < 2.1@@ -452,10 +481,12 @@ , monad-logger >= 0.3 && < 0.4 , fast-logger >= 2.2 && < 2.3 , wai-logger >= 2.2 && < 2.3-- -- see https://github.com/yesodweb/yesod/issues/814- if !os(windows)- build-depends: unix+ , file-embed+ , safe+ , unordered-containers+ , containers+ , vector+ , time executable PROJECTNAME if flag(library-only)@@ -463,9 +494,7 @@ main-is: main.hs hs-source-dirs: app- build-depends: base- , PROJECTNAME- , yesod+ build-depends: base, PROJECTNAME ghc-options: -threaded -O2 -rtsopts -with-rtsopts=-N @@ -493,7 +522,7 @@ build-depends: base , PROJECTNAME- , yesod-test >= 1.4 && < 1.5+ , yesod-test >= 1.4.2 && < 1.5 , yesod-core , yesod , persistent@@ -502,6 +531,8 @@ , monad-logger , transformers , hspec+ , classy-prelude+ , classy-prelude-yesod {-# START_FILE Settings.hs #-} -- | Settings are centralized, as much as possible, into this file. This@@ -511,45 +542,81 @@ -- declared in the Foundation.hs file. module Settings where -import Prelude-import Text.Shakespeare.Text (st)-import Language.Haskell.TH.Syntax-import Database.Persist.Sqlite (SqliteConf)-import Yesod.Default.Config-import Yesod.Default.Util-import Data.Text (Text)-import Data.Yaml-import Control.Applicative-import Settings.Development-import Data.Default (def)-import Text.Hamlet+import ClassyPrelude.Yesod+import Control.Exception (throw)+import Data.Aeson (Result (..), fromJSON, withObject, (.!=),+ (.:?))+import Data.FileEmbed (embedFile)+import Data.Yaml (decodeEither')+import Database.Persist.Sqlite (SqliteConf)+import Language.Haskell.TH.Syntax (Exp, Name, Q)+import Network.Wai.Handler.Warp (HostPreference)+import Yesod.Default.Config2 (applyEnvValue, configSettingsYml)+import Yesod.Default.Util (WidgetFileSettings, widgetFileNoReload,+ widgetFileReload) --- | Which Persistent backend this site is using.-type PersistConf = SqliteConf+-- | Runtime settings to configure this application. These settings can be+-- loaded from various sources: defaults, environment variables, config files,+-- theoretically even a database.+data AppSettings = AppSettings+ { appStaticDir :: String+ -- ^ Directory from which to serve static files.+ , appDatabaseConf :: SqliteConf+ -- ^ Configuration settings for accessing the database.+ , appRoot :: Text+ -- ^ Base for all generated URLs.+ , appHost :: HostPreference+ -- ^ Host/interface the server should bind to.+ , appPort :: Int+ -- ^ Port to listen on+ , appIpFromHeader :: Bool+ -- ^ Get the IP address from the header when logging. Useful when sitting+ -- behind a reverse proxy. --- Static setting below. Changing these requires a recompile+ , appDetailedRequestLogging :: Bool+ -- ^ Use detailed request logging system+ , appShouldLogAll :: Bool+ -- ^ Should all log messages be displayed?+ , appReloadTemplates :: Bool+ -- ^ Use the reload version of templates+ , appMutableStatic :: Bool+ -- ^ Assume that files in the static dir may change after compilation+ , appSkipCombining :: Bool+ -- ^ Perform no stylesheet/script combining --- | 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-staticDir = "static"+ -- Example app-specific configuration values.+ , appCopyright :: Text+ -- ^ Copyright text to appear in the footer of the page+ , appAnalytics :: Maybe Text+ -- ^ Google Analytics code+ } --- | The base URL for your static files. As you can see by the default--- value, this can simply be "static" appended to your application root.--- A powerful optimization can be serving static files from a separate--- domain name. This allows you to use a web server optimized for static--- files, more easily set expires and cache values, and avoid possibly--- costly transference of cookies on static files. For more information,--- please see:--- http://code.google.com/speed/page-speed/docs/request.html#ServeFromCookielessDomain------ If you change the resource pattern for StaticR in Foundation.hs, you will--- have to make a corresponding change here.------ To see how this value is used, see urlRenderOverride in Foundation.hs-staticRoot :: AppConfig DefaultEnv x -> Text-staticRoot conf = [st|#{appRoot conf}/static|]+instance FromJSON AppSettings where+ parseJSON = withObject "AppSettings" $ \o -> do+ let defaultDev =+#if DEVELOPMENT+ True+#else+ False+#endif+ appStaticDir <- o .: "static-dir"+ appDatabaseConf <- o .: "database"+ appRoot <- o .: "approot"+ appHost <- fromString <$> o .: "host"+ appPort <- o .: "port"+ appIpFromHeader <- o .: "ip-from-header" + appDetailedRequestLogging <- o .:? "detailed-logging" .!= defaultDev+ appShouldLogAll <- o .:? "should-log-all" .!= defaultDev+ appReloadTemplates <- o .:? "reload-templates" .!= defaultDev+ appMutableStatic <- o .:? "mutable-static" .!= defaultDev+ appSkipCombining <- o .:? "skip-combining" .!= defaultDev++ appCopyright <- o .: "copyright"+ appAnalytics <- o .:? "analytics"++ return AppSettings {..}+ -- | Settings for 'widgetFile', such as which template languages to support and -- default Hamlet settings. --@@ -558,81 +625,62 @@ -- https://github.com/yesodweb/yesod/wiki/Overriding-widgetFile widgetFileSettings :: WidgetFileSettings widgetFileSettings = def- { wfsHamletSettings = defaultHamletSettings- { hamletNewlines = AlwaysNewlines- }- } +-- | How static files should be combined.+combineSettings :: CombineSettings+combineSettings = def+ -- The rest of this file contains settings which rarely need changing by a -- user. widgetFile :: String -> Q Exp-widgetFile = (if development then widgetFileReload- else widgetFileNoReload)+widgetFile = (if appReloadTemplates compileTimeAppSettings+ then widgetFileReload+ else widgetFileNoReload) widgetFileSettings -data Extra = Extra- { extraCopyright :: Text- , extraAnalytics :: Maybe Text -- ^ Google Analytics- } deriving Show+-- | Raw bytes at compile time of @config/settings.yml@+configSettingsYmlBS :: ByteString+configSettingsYmlBS = $(embedFile configSettingsYml) -parseExtra :: DefaultEnv -> Object -> Parser Extra-parseExtra _ o = Extra- <$> o .: "copyright"- <*> o .:? "analytics"+-- | @config/settings.yml@, parsed to a @Value@.+configSettingsYmlValue :: Value+configSettingsYmlValue = either throw id $ decodeEither' configSettingsYmlBS -{-# START_FILE Settings/Development.hs #-}-module Settings.Development where+-- | A version of @AppSettings@ parsed at compile time from @config/settings.yml@.+compileTimeAppSettings :: AppSettings+compileTimeAppSettings =+ case fromJSON $ applyEnvValue False mempty configSettingsYmlValue of+ Error e -> error e+ Success settings -> settings -import Prelude+-- The following two functions can be used to combine multiple CSS or JS files+-- at compile time to decrease the number of http requests.+-- Sample usage (inside a Widget):+--+-- > $(combineStylesheets 'StaticR [style1_css, style2_css]) -development :: Bool-development =-#if DEVELOPMENT- True-#else- False-#endif+combineStylesheets :: Name -> [Route Static] -> Q Exp+combineStylesheets = combineStylesheets'+ (appSkipCombining compileTimeAppSettings)+ combineSettings -production :: Bool-production = not development+combineScripts :: Name -> [Route Static] -> Q Exp+combineScripts = combineScripts'+ (appSkipCombining compileTimeAppSettings)+ combineSettings {-# START_FILE Settings/StaticFiles.hs #-} module Settings.StaticFiles where -import Prelude (IO)-import Yesod.Static-import qualified Yesod.Static as Static-import Settings (staticDir)-import Settings.Development-import Language.Haskell.TH (Q, Exp, Name)-import Data.Default (def)---- | use this to create your static file serving site-staticSite :: IO Static.Static-staticSite = if development then Static.staticDevel staticDir- else Static.static staticDir+import Settings (appStaticDir, compileTimeAppSettings)+import Yesod.Static (staticFiles) -- | This generates easy references to files in the static directory at compile time, -- giving you compile-time verification that referenced files exist. -- Warning: any files added to your static directory during run-time can't be -- accessed this way. You'll have to use their FilePath or URL to access them.-$(staticFiles Settings.staticDir)--combineSettings :: CombineSettings-combineSettings = def---- The following two functions can be used to combine multiple CSS or JS files--- at compile time to decrease the number of http requests.--- Sample usage (inside a Widget):------ > $(combineStylesheets 'StaticR [style1_css, style2_css])--combineStylesheets :: Name -> [Route Static] -> Q Exp-combineStylesheets = combineStylesheets' development combineSettings--combineScripts :: Name -> [Route Static] -> Q Exp-combineScripts = combineScripts' development combineSettings+staticFiles (appStaticDir compileTimeAppSettings) {-# START_FILE app/DevelMain.hs #-} -- | Development version to be run inside GHCi.@@ -699,19 +747,24 @@ start :: MVar () -- ^ Written to when the thread is killed. -> IO ThreadId start done = do- (port,app) <- getApplicationDev- forkIO (finally (runSettings (setPort port defaultSettings) app)+ (settings,app) <- getApplicationDev+ forkIO (finally (runSettings settings app) (putMVar done ())) +{-# START_FILE app/devel.hs #-}+{-# LANGUAGE PackageImports #-}+import "PROJECTNAME" Application (develMain)+import Prelude (IO)++main :: IO ()+main = develMain+ {-# START_FILE app/main.hs #-}-import Prelude (IO)-import Yesod.Default.Config (fromArgs)-import Yesod.Default.Main (defaultMainLog)-import Settings (parseExtra)-import Application (makeApplication)+import Prelude (IO)+import Application (appMain) main :: IO ()-main = defaultMainLog (fromArgs parseExtra) makeApplication+main = appMain {-# START_FILE BASE64 config/favicon.ico #-} AAABAAIAEBAAAAEAIABoBAAAJgAAABAQAgABAAEAsAAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAA@@ -820,7 +873,7 @@ User-agent: * {-# START_FILE config/routes #-}-/static StaticR Static getStatic+/static StaticR Static appStatic /auth AuthR Auth getAuth /favicon.ico FaviconR GET@@ -829,25 +882,28 @@ / HomeR GET POST {-# START_FILE config/settings.yml #-}-Default: &defaults- host: "*4" # any IPv4 host- port: 3000- approot: "http://localhost:3000"- copyright: Insert copyright statement here- #analytics: UA-YOURCODE--Development:- <<: *defaults+static-dir: "_env:STATIC_DIR:static"+host: "_env:HOST:*4" # any IPv4 host+port: "_env:PORT:3000"+approot: "_env:APPROOT:http://localhost:3000"+ip-from-header: "_env:IP_FROM_HEADER:false" -Testing:- <<: *defaults+# Optional values with the following production defaults.+# In development, they default to the inverse.+#+# development: false+# detailed-logging: false+# should-log-all: false+# reload-templates: false+# mutable-static: false+# skip-combining: false -Staging:- <<: *defaults+database:+ database: "_env:SQLITE_DATABASE:PROJECTNAME.sqlite3"+ poolsize: "_env:SQLITE_POOLSIZE:10" -Production:- #approot: "http://www.example.com"- <<: *defaults+copyright: Insert copyright statement here+#analytics: UA-YOURCODE {-# START_FILE config/sqlite.yml #-} Default: &defaults@@ -871,135 +927,9 @@ poolsize: 100 <<: *defaults -{-# START_FILE deploy/Procfile #-}-# Free deployment to Heroku.-#-# !! Warning: You must use a 64 bit machine to compile !!-#-# This could mean using a virtual machine. Give your VM as much memory as you can to speed up linking.-#-# Basic Yesod setup:-#-# * Move this file out of the deploy directory and into your root directory-#-# mv deploy/Procfile ./-#-# * Create an empty package.json-# echo '{ "name": "PROJECTNAME", "version": "0.0.1", "dependencies": {} }' >> package.json-#-# Postgresql Yesod setup:-#-# * add dependencies on the "heroku", "aeson" and "unordered-containers" packages in your cabal file-#-# * add code in Application.hs to use the heroku package and load the connection parameters.-# The below works for Postgresql.-#-# import Data.HashMap.Strict as H-# import Data.Aeson.Types as AT-# #ifndef DEVELOPMENT-# import qualified Web.Heroku-# #endif-#-#-#-# makeFoundation :: AppConfig DefaultEnv Extra -> Logger -> IO App-# makeFoundation conf setLogger = do-# manager <- newManager def-# s <- staticSite-# hconfig <- loadHerokuConfig-# dbconf <- withYamlEnvironment "config/postgresql.yml" (appEnv conf)-# (Database.Persist.Store.loadConfig . combineMappings hconfig) >>=-# Database.Persist.Store.applyEnv-# p <- Database.Persist.Store.createPoolConfig (dbconf :: Settings.PersistConfig)-# Database.Persist.Store.runPool dbconf (runMigration migrateAll) p-# return $ App conf setLogger s p manager dbconf-#-# #ifndef DEVELOPMENT-# canonicalizeKey :: (Text, val) -> (Text, val)-# canonicalizeKey ("dbname", val) = ("database", val)-# canonicalizeKey pair = pair-#-# toMapping :: [(Text, Text)] -> AT.Value-# toMapping xs = AT.Object $ M.fromList $ map (\(key, val) -> (key, AT.String val)) xs-# #endif-#-# combineMappings :: AT.Value -> AT.Value -> AT.Value-# combineMappings (AT.Object m1) (AT.Object m2) = AT.Object $ m1 `M.union` m2-# combineMappings _ _ = error "Data.Object is not a Mapping."-#-# loadHerokuConfig :: IO AT.Value-# loadHerokuConfig = do-# #ifdef DEVELOPMENT-# return $ AT.Object M.empty-# #else-# Web.Heroku.dbConnParams >>= return . toMapping . map canonicalizeKey-# #endif----# Heroku setup:-# Find the Heroku guide. Roughly:-#-# * sign up for a heroku account and register your ssh key-# * create a new application on the *cedar* stack-#-# * make your Yesod project the git repository for that application-# * create a deploy branch-#-# git checkout -b deploy-#-# Repeat these steps to deploy:-# * add your web executable binary (referenced below) to the git repository-#-# git checkout deploy-# git add ./dist/build/PROJECTNAME/PROJECTNAME-# git commit -m deploy-#-# * push to Heroku-#-# git push heroku deploy:master---# Heroku configuration that runs your app-web: ./dist/build/PROJECTNAME/PROJECTNAME production -p $PORT--{-# START_FILE devel.hs #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE PackageImports #-}-import "PROJECTNAME" Application (getApplicationDev)-import Network.Wai.Handler.Warp- (runSettings, defaultSettings, setPort)-import Control.Concurrent (forkIO)-import System.Directory (doesFileExist, removeFile)-import System.Exit (exitSuccess)-import Control.Concurrent (threadDelay)--#ifndef mingw32_HOST_OS-import System.Posix.Signals (installHandler, sigINT, Handler(Catch))-#endif--main :: IO ()-main = do-#ifndef mingw32_HOST_OS- _ <- installHandler sigINT (Catch $ return ()) Nothing-#endif-- putStrLn "Starting devel application"- (port, app) <- getApplicationDev- forkIO $ runSettings (setPort port defaultSettings) app- loop--loop :: IO ()-loop = do- threadDelay 100000- e <- doesFileExist "yesod-devel/devel-terminate"- if e then terminateDevel else loop--terminateDevel :: IO ()-terminateDevel = exitSuccess--{-# START_FILE messages/en.msg #-}-Hello: Hello+{-# START_FILE config/test-settings.yml #-}+database:+ database: PROJECTNAME_test.sqlite3 {-# START_FILE static/css/bootstrap.css #-} /*!@@ -8965,9 +8895,9 @@ <div id="main" role="main"> ^{pageBody pc} <footer>- #{extraCopyright $ appExtra $ settings master}+ #{appCopyright $ appSettings master} - $maybe analytics <- extraAnalytics $ appExtra $ settings master+ $maybe analytics <- appAnalytics $ appSettings master <script> if(!window.location.href.match(/localhost/)){ window._gaq = [['_setAccount','#{analytics}'],['_trackPageview'],['_trackPageLoadTime']];@@ -8990,7 +8920,7 @@ ^{widget} {-# START_FILE templates/homepage.hamlet #-}-<h1>_{MsgHello}+<h1>Welcome to Yesod! <ol> <li>Now that you have a working project you should use the #@@ -9041,92 +8971,88 @@ color: #990 } -{-# START_FILE test/Handler/HomeSpec.hs #-}-module Handler.HomeSpec- ( spec- ) where+{-# START_FILE test/Handler/CommonSpec.hs #-}+module Handler.CommonSpec (spec) where import TestImport-import qualified Data.List as L spec :: Spec-spec =- ydescribe "These are some example tests" $ do-- yit "loads the index and checks it looks right" $ do- get HomeR+spec = withApp $ do+ describe "robots.txt" $ do+ it "gives a 200" $ do+ get RobotsR statusIs 200- htmlAllContain "h1" "Hello"+ it "has correct User-agent" $ do+ get RobotsR+ bodyContains "User-agent: *"+ describe "favicon.ico" $ do+ it "gives a 200" $ do+ get FaviconR+ statusIs 200 - request $ do- setMethod "POST"- setUrl HomeR- addNonce- fileByLabel "Choose a file" "test/Spec.hs" "text/plain" -- talk about self-reference- byLabel "What's on the file?" "Some Content"+{-# START_FILE test/Handler/HomeSpec.hs #-}+module Handler.HomeSpec (spec) where - statusIs 200- -- more debugging printBody- htmlCount ".message" 1- htmlAllContain ".message" "Some Content"- htmlAllContain ".message" "text/plain"+import TestImport - -- This is a simple example of using a database access in a test. The- -- test will succeed for a fresh scaffolded site with an empty database,- -- but will fail on an existing database with a non-empty user table.- yit "leaves the user table empty" $ do- get HomeR- statusIs 200- users <- runDB $ selectList ([] :: [Filter User]) []- assertEqual "user table empty" 0 $ L.length users+spec :: Spec+spec = withApp $ do+ it "loads the index and checks it looks right" $ do+ get HomeR+ statusIs 200+ htmlAllContain "h1" "Welcome to Yesod" -{-# START_FILE test/Spec.hs #-}-module Main where+ request $ do+ setMethod "POST"+ setUrl HomeR+ addNonce+ fileByLabel "Choose a file" "test/Spec.hs" "text/plain" -- talk about self-reference+ byLabel "What's on the file?" "Some Content" -import Import-import Yesod.Default.Config-import Yesod.Test-import Test.Hspec (hspec)-import Application (makeFoundation)+ statusIs 200+ -- more debugging printBody+ htmlCount ".message" 1+ htmlAllContain ".message" "Some Content"+ htmlAllContain ".message" "text/plain" -import qualified Handler.HomeSpec+ -- This is a simple example of using a database access in a test. The+ -- test will succeed for a fresh scaffolded site with an empty database,+ -- but will fail on an existing database with a non-empty user table.+ it "leaves the user table empty" $ do+ get HomeR+ statusIs 200+ users <- runDB $ selectList ([] :: [Filter User]) []+ assertEqual "user table empty" 0 $ length users -main :: IO ()-main = do- conf <- Yesod.Default.Config.loadConfig $ (configSettings Testing)- { csParseExtra = parseExtra- }- foundation <- makeFoundation conf- hspec $ do- yesodSpec foundation $ do- Handler.HomeSpec.spec+{-# START_FILE test/Spec.hs #-}+{-# OPTIONS_GHC -F -pgmF hspec-discover #-} {-# START_FILE test/TestImport.hs #-} module TestImport- ( module Yesod.Test- , module Model- , module Foundation- , module Database.Persist- , module Prelude- , runDB- , Spec- , Example+ ( module TestImport+ , module X ) where -import Yesod.Test-import Database.Persist hiding (get)-import Database.Persist.Sql (SqlPersistM, runSqlPersistMPool)-import Control.Monad.IO.Class (liftIO)-import Prelude--import Foundation-import Model--type Spec = YesodSpec App-type Example = YesodExample App+import Application (makeFoundation)+import ClassyPrelude as X+import Database.Persist as X hiding (get)+import Database.Persist.Sql (SqlPersistM, runSqlPersistMPool)+import Foundation as X+import Model as X+import Test.Hspec as X+import Yesod.Default.Config2 (ignoreEnv, loadAppSettings)+import Yesod.Test as X -runDB :: SqlPersistM a -> Example a+runDB :: SqlPersistM a -> YesodExample App a runDB query = do- pool <- fmap connPool getTestYesod+ pool <- fmap appConnPool getTestYesod liftIO $ runSqlPersistMPool query pool++withApp :: SpecWith App -> Spec+withApp = before $ do+ settings <- loadAppSettings+ ["config/test-settings.yml", "config/settings.yml"]+ []+ ignoreEnv+ makeFoundation settings
yesod-bin.cabal view
@@ -1,5 +1,5 @@ name: yesod-bin-version: 1.4.0.9+version: 1.4.1 license: MIT license-file: LICENSE author: Michael Snoyman <michael@snoyman.com>