packages feed

sydtest-yesod-0.3.0.2: blog-example/Example/Blog.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ViewPatterns #-}

-- This is an example of a yesod application that uses a database.
-- It is supposed to represent a blog with blog posts.
-- However, we call the blog posts thought so that it's not too confusing with 'POST' also being the HTTP method.
module Example.Blog where

import Control.Monad.Logger
import Data.Text (Text)
import Database.Persist.Sql
import Database.Persist.Sqlite
import Database.Persist.TH
import Yesod

share
  [mkPersist sqlSettings, mkMigrate "migrateThoughts"]
  [persistLowerCase|
Thought
    title Text
    contents Text
    deriving Show Eq
|]

data App = App
  { appConnectionPool :: !ConnectionPool,
    appSessionKeyFile :: !FilePath
  }

mkYesod
  "App"
  [parseRoutes|
    / HomeR GET
    /new-thought NewThoughtR GET POST
    /thought/#ThoughtId ThoughtR GET
|]

type Form x = Html -> MForm (HandlerFor App) (FormResult x, Widget)

instance Yesod App where
  makeSessionBackend App {..} = Just <$> defaultClientSessionBackend 30 appSessionKeyFile

instance YesodPersist App where
  type YesodPersistBackend App = SqlBackend
  runDB func = do
    pool <- getsYesod appConnectionPool
    runSqlPool func pool

instance RenderMessage App FormMessage where
  renderMessage _ _ = defaultFormMessage

getHomeR :: Handler Html
getHomeR = defaultLayout "Welcome!, feel free to post some thoughts at /new-thought."

data NewThought = NewThought
  { newThoughtTitle :: Text,
    newThoughtContents :: Text
  }

newThoughtForm :: Form NewThought
newThoughtForm =
  renderDivs $
    NewThought
      <$> areq textField ("Title" {fsName = Just "title"}) Nothing
      <*> (unTextarea <$> areq textareaField ("Contents" {fsName = Just "contents"}) Nothing)

getNewThoughtR :: Handler Html
getNewThoughtR = do
  ((res, form), enctype) <- runFormPost newThoughtForm
  case res of
    FormSuccess f -> do
      thoughtId <-
        runDB $
          insert $
            Thought
              { thoughtTitle = newThoughtTitle f,
                thoughtContents = newThoughtContents f
              }
      defaultLayout
        [whamlet|
          <p>You've posted a thought!
          <p>the title was #{newThoughtTitle f}
          <p>the contents were #{newThoughtContents f}
          <a href=@{ThoughtR thoughtId}>
            Look at your thought
        |]
    FormMissing ->
      defaultLayout
        [whamlet|
          <p>Hello world!
          <form enctype="#{enctype}" method="post">
            ^{form}
            <input type="submit">
              Submit thought
        |]
    FormFailure errs -> invalidArgs errs

postNewThoughtR :: Handler Html
postNewThoughtR = getNewThoughtR

getThoughtR :: ThoughtId -> Handler Html
getThoughtR thoughtId = do
  thought <- runDB $ get404 thoughtId
  defaultLayout
    [whamlet|
      <h1> #{thoughtTitle thought}
      <p>
        #{thoughtContents thought}
    |]

main :: IO ()
main = runStderrLoggingT $
  withSqlitePool "example.sqlite3" 1 $ \pool -> do
    runSqlPool (runMigration migrateThoughts) pool
    liftIO $
      Yesod.warp 3000 $
        App
          { appConnectionPool = pool,
            appSessionKeyFile = "client_session_key.aes"
          }