123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- {-# LANGUAGE ConstraintKinds #-}
- {-# LANGUAGE TypeFamilies #-}
- {-# LANGUAGE CPP #-}
- {-# LANGUAGE FlexibleContexts #-}
- {-# LANGUAGE RankNTypes #-}
- {-# OPTIONS_GHC -fno-warn-orphans #-}
- -- | Defines the core functionality of this package. This package is
- -- distinguished from Yesod.Persist in that the latter additionally exports the
- -- persistent modules themselves.
- module Yesod.Persist.Core
- ( YesodPersist (..)
- , defaultRunDB
- , YesodPersistRunner (..)
- , defaultGetDBRunner
- , DBRunner (..)
- , runDBSource
- , respondSourceDB
- , YesodDB
- , get404
- , getBy404
- ) where
- import Database.Persist
- import Control.Monad.Trans.Reader (ReaderT, runReaderT, withReaderT)
- import Yesod.Core
- import Data.Conduit
- import Blaze.ByteString.Builder (Builder)
- import Data.Pool
- import Control.Monad.Trans.Resource
- import Control.Exception (throwIO)
- import Yesod.Core.Types (HandlerContents (HCError))
- import qualified Database.Persist.Sql as SQL
- unSqlPersistT :: a -> a
- unSqlPersistT = id
- type YesodDB site = ReaderT (YesodPersistBackend site) (HandlerT site IO)
- class Monad (YesodDB site) => YesodPersist site where
- type YesodPersistBackend site
- runDB :: YesodDB site a -> HandlerT site IO a
- -- | Helper for creating 'runDB'.
- --
- -- Since 1.2.0
- defaultRunDB :: PersistConfig c
- => (site -> c)
- -> (site -> PersistConfigPool c)
- -> PersistConfigBackend c (HandlerT site IO) a
- -> HandlerT site IO a
- defaultRunDB getConfig getPool f = do
- master <- getYesod
- Database.Persist.runPool
- (getConfig master)
- f
- (getPool master)
- -- |
- --
- -- Since 1.2.0
- class YesodPersist site => YesodPersistRunner site where
- -- | This function differs from 'runDB' in that it returns a database
- -- runner function, as opposed to simply running a single action. This will
- -- usually mean that a connection is taken from a pool and then reused for
- -- each invocation. This can be useful for creating streaming responses;
- -- see 'runDBSource'.
- --
- -- It additionally returns a cleanup function to free the connection. If
- -- your code finishes successfully, you /must/ call this cleanup to
- -- indicate changes should be committed. Otherwise, for SQL backends at
- -- least, a rollback will be used instead.
- --
- -- Since 1.2.0
- getDBRunner :: HandlerT site IO (DBRunner site, HandlerT site IO ())
- newtype DBRunner site = DBRunner
- { runDBRunner :: forall a. YesodDB site a -> HandlerT site IO a
- }
- -- | Helper for implementing 'getDBRunner'.
- --
- -- Since 1.2.0
- #if MIN_VERSION_persistent(2,5,0)
- defaultGetDBRunner :: (SQL.IsSqlBackend backend, YesodPersistBackend site ~ backend)
- => (site -> Pool backend)
- -> HandlerT site IO (DBRunner site, HandlerT site IO ())
- #else
- defaultGetDBRunner :: YesodPersistBackend site ~ SQL.SqlBackend
- => (site -> Pool SQL.SqlBackend)
- -> HandlerT site IO (DBRunner site, HandlerT site IO ())
- #endif
- defaultGetDBRunner getPool = do
- pool <- fmap getPool getYesod
- let withPrep conn f = f (persistBackend conn) (SQL.connPrepare $ persistBackend conn)
- (relKey, (conn, local)) <- allocate
- (do
- (conn, local) <- takeResource pool
- withPrep conn SQL.connBegin
- return (conn, local)
- )
- (\(conn, local) -> do
- withPrep conn SQL.connRollback
- destroyResource pool local conn)
- let cleanup = liftIO $ do
- withPrep conn SQL.connCommit
- putResource local conn
- _ <- unprotect relKey
- return ()
- return (DBRunner $ \x -> runReaderT (unSqlPersistT x) conn, cleanup)
- -- | Like 'runDB', but transforms a @Source@. See 'respondSourceDB' for an
- -- example, practical use case.
- --
- -- Since 1.2.0
- runDBSource :: YesodPersistRunner site
- => Source (YesodDB site) a
- -> Source (HandlerT site IO) a
- runDBSource src = do
- (dbrunner, cleanup) <- lift getDBRunner
- transPipe (runDBRunner dbrunner) src
- lift cleanup
- -- | Extends 'respondSource' to create a streaming database response body.
- respondSourceDB :: YesodPersistRunner site
- => ContentType
- -> Source (YesodDB site) (Flush Builder)
- -> HandlerT site IO TypedContent
- respondSourceDB ctype = respondSource ctype . runDBSource
- -- | Get the given entity by ID, or return a 404 not found if it doesn't exist.
- #if MIN_VERSION_persistent(2,5,0)
- get404 :: (MonadIO m, PersistStore backend, PersistRecordBackend val backend)
- => Key val
- -> ReaderT backend m val
- #else
- get404 :: (MonadIO m, PersistStore (PersistEntityBackend val), PersistEntity val)
- => Key val
- -> ReaderT (PersistEntityBackend val) m val
- #endif
- get404 key = do
- mres <- get key
- case mres of
- Nothing -> notFound'
- Just res -> return res
- -- | Get the given entity by unique key, or return a 404 not found if it doesn't
- -- exist.
- #if MIN_VERSION_persistent(2,5,0)
- getBy404 :: (PersistUnique backend, PersistRecordBackend val backend, MonadIO m)
- => Unique val
- -> ReaderT backend m (Entity val)
- #else
- getBy404 :: (PersistUnique (PersistEntityBackend val), PersistEntity val, MonadIO m)
- => Unique val
- -> ReaderT (PersistEntityBackend val) m (Entity val)
- #endif
- getBy404 key = do
- mres <- getBy key
- case mres of
- Nothing -> notFound'
- Just res -> return res
- -- | Should be equivalent to @lift . notFound@, but there's an apparent bug in
- -- GHC 7.4.2 that leads to segfaults. This is a workaround.
- notFound' :: MonadIO m => m a
- notFound' = liftIO $ throwIO $ HCError NotFound
|