packages feed

persistent 0.3.1.3 → 0.4.0

raw patch · 8 files changed

+432/−142 lines, 8 filesdep +HDBCdep +HDBC-postgresqldep +HUnitdep ~bytestringdep ~template-haskelldep ~textnew-component:exe:runtests

Dependencies added: HDBC, HDBC-postgresql, HUnit, blaze-html, haskell98, monad-peel, pool, test-framework, test-framework-hunit, utf8-string

Dependency ranges changed: bytestring, template-haskell, text, web-routes-quasi

Files

Database/Persist/Base.hs view
@@ -38,7 +38,8 @@ import Data.Typeable (Typeable) import Data.Int (Int8, Int16, Int32, Int64) import Data.Word (Word8, Word16, Word32, Word64)-import Text.Hamlet+import Text.Blaze (Html, unsafeByteString)+import Text.Blaze.Renderer.Utf8 (renderHtml) import qualified Data.Text as T import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L
Database/Persist/GenericSql.hs view
@@ -27,7 +27,7 @@ import Control.Monad.IO.Class import Control.Monad.Trans.Reader import Control.Monad.Trans.Class (MonadTrans (..))-import Database.Persist.Pool+import Data.Pool import Control.Monad.Trans.Writer import System.IO import Database.Persist.GenericSql.Internal@@ -36,22 +36,23 @@ import Control.Monad (liftM, unless) import Data.Enumerator (Stream (..), Iteratee (..), Step (..)) import Language.Haskell.TH.Syntax hiding (lift)-import Control.Monad.Invert (MonadInvertIO, onException)+import Control.Monad.IO.Peel (MonadPeelIO)+import Control.Exception.Peel (onException) import Control.Exception (toException)  type ConnectionPool = Pool Connection -withStmt' :: MonadInvertIO m => String -> [PersistValue]+withStmt' :: MonadPeelIO m => String -> [PersistValue]          -> (RowPopper (SqlPersist m) -> SqlPersist m a) -> SqlPersist m a withStmt' = R.withStmt  execute' :: MonadIO m => String -> [PersistValue] -> SqlPersist m () execute' = R.execute -runSqlPool :: MonadInvertIO m => SqlPersist m a -> Pool Connection -> m a+runSqlPool :: MonadPeelIO m => SqlPersist m a -> Pool Connection -> m a runSqlPool r pconn = withPool' pconn $ runSqlConn r -runSqlConn :: MonadInvertIO m => SqlPersist m a -> Connection -> m a+runSqlConn :: MonadPeelIO m => SqlPersist m a -> Connection -> m a runSqlConn (SqlPersist r) conn = do     let getter = R.getStmt' conn     liftIO $ begin conn getter@@ -61,7 +62,7 @@     liftIO $ commit conn getter     return x -instance MonadInvertIO m => PersistBackend (SqlPersist m) where+instance MonadPeelIO m => PersistBackend (SqlPersist m) where     insert val = do         conn <- SqlPersist ask         let esql = insertSql conn (rawTableName t) (map fst3 $ tableColumns t)@@ -456,29 +457,29 @@       Left errs -> error $ unlines errs       Right sql -> return sql -printMigration :: MonadInvertIO m => Migration (SqlPersist m) -> SqlPersist m ()+printMigration :: MonadPeelIO m => Migration (SqlPersist m) -> SqlPersist m () printMigration m = do   mig <- parseMigration' m   mapM_ (liftIO . putStrLn) (allSql mig) -getMigration :: MonadInvertIO m => Migration (SqlPersist m) -> SqlPersist m [Sql]+getMigration :: MonadPeelIO m => Migration (SqlPersist m) -> SqlPersist m [Sql] getMigration m = do   mig <- parseMigration' m   return $ allSql mig -runMigration :: MonadInvertIO m+runMigration :: MonadPeelIO m              => Migration (SqlPersist m)              -> SqlPersist m () runMigration m = runMigration' m False >> return ()  -- | Same as 'runMigration', but returns a list of the SQL commands executed -- instead of printing them to stderr.-runMigrationSilent :: MonadInvertIO m+runMigrationSilent :: MonadPeelIO m                    => Migration (SqlPersist m)                    -> SqlPersist m [String] runMigrationSilent m = runMigration' m True -runMigration' :: MonadInvertIO m+runMigration' :: MonadPeelIO m               => Migration (SqlPersist m)               -> Bool -- ^ is silent?               -> SqlPersist m [String]@@ -492,7 +493,7 @@             , unlines $ map (\s -> "    " ++ s ++ ";") $ errs             ] -runMigrationUnsafe :: MonadInvertIO m+runMigrationUnsafe :: MonadPeelIO m                    => Migration (SqlPersist m)                    -> SqlPersist m () runMigrationUnsafe m = do@@ -505,7 +506,7 @@     execute' s []     return s -migrate :: (MonadInvertIO m, PersistEntity val)+migrate :: (MonadPeelIO m, PersistEntity val)         => val         -> Migration (SqlPersist m) migrate val = do@@ -535,7 +536,7 @@         ]   where     typ = ForallT [PlainTV $ mkName "m"]-            [ ClassP ''MonadInvertIO [VarT $ mkName "m"]+            [ ClassP ''MonadPeelIO [VarT $ mkName "m"]             ]             $ ConT ''Migration `AppT` (ConT ''SqlPersist `AppT` VarT (mkName "m"))     body =
Database/Persist/GenericSql/Internal.hs view
@@ -20,11 +20,12 @@ import qualified Data.Map as Map import Data.IORef import Control.Monad.IO.Class-import Database.Persist.Pool+import Data.Pool import Database.Persist.Base import Data.Maybe (fromJust) import Control.Arrow-import Control.Monad.Invert (MonadInvertIO, bracket)+import Control.Monad.IO.Peel (MonadPeelIO)+import Control.Exception.Peel (bracket) import Database.Persist.TH (nullable)  type RowPopper m = m (Maybe [PersistValue])@@ -47,15 +48,15 @@     { finalize :: IO ()     , reset :: IO ()     , execute :: [PersistValue] -> IO ()-    , withStmt :: forall a m. MonadInvertIO m+    , withStmt :: forall a m. MonadPeelIO m                => [PersistValue] -> (RowPopper m -> m a) -> m a     } -withSqlPool :: MonadInvertIO m+withSqlPool :: MonadPeelIO m             => IO Connection -> Int -> (Pool Connection -> m a) -> m a withSqlPool mkConn = createPool mkConn close' -withSqlConn :: MonadInvertIO m => IO Connection -> (Connection -> m a) -> m a+withSqlConn :: MonadPeelIO m => IO Connection -> (Connection -> m a) -> m a withSqlConn open = bracket (liftIO open) (liftIO . close')  close' :: Connection -> IO ()
Database/Persist/GenericSql/Raw.hs view
@@ -18,22 +18,12 @@ import qualified Data.Map as Map import Control.Applicative (Applicative) import Control.Monad.Trans.Class (MonadTrans (..))-import Control.Monad.Invert (MonadInvertIO (..))-import Control.Monad (liftM)+import Control.Monad.IO.Peel (MonadPeelIO (..))  newtype SqlPersist m a = SqlPersist { unSqlPersist :: ReaderT Connection m a }-    deriving (Monad, MonadIO, MonadTrans, Functor, Applicative)--instance MonadInvertIO m => MonadInvertIO (SqlPersist m) where-    newtype InvertedIO (SqlPersist m) a =-        InvSqlPersistIO-            { runInvSqlPersistIO :: InvertedIO (ReaderT Connection m) a-            }-    type InvertedArg (SqlPersist m) = (Connection, InvertedArg m)-    invertIO = liftM (fmap InvSqlPersistIO) . invertIO . unSqlPersist-    revertIO f = SqlPersist $ revertIO $ liftM runInvSqlPersistIO . f+    deriving (Monad, MonadIO, MonadTrans, Functor, Applicative, MonadPeelIO) -withStmt :: MonadInvertIO m => String -> [PersistValue]+withStmt :: MonadPeelIO m => String -> [PersistValue]          -> (RowPopper (SqlPersist m) -> SqlPersist m a) -> SqlPersist m a withStmt sql vals pop = do     stmt <- getStmt sql
− Database/Persist/Pool.hs
@@ -1,92 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE PackageImports #-}-module Database.Persist.Pool-    ( -- * Using pools-      createPool-    , withPool-    , withPool'-    , Pool-      -- * Diagnostics-    , PoolStats (..)-    , poolStats-    ) where--import Control.Concurrent.STM (atomically)-import Control.Concurrent.STM.TVar-    (TVar, newTVarIO, readTVar, writeTVar)-import Control.Exception (throwIO, Exception)-import Data.Typeable-import qualified Control.Monad.Invert as I-import Control.Monad.IO.Class-import Control.Monad--data PoolData a = PoolData-    { poolAvail :: [a]-    , poolCreated :: Int-    }--data Pool a = Pool-    { poolMax :: Int-    , poolData :: TVar (PoolData a)-    , poolMake :: IO a-    }--data PoolStats = PoolStats-    { poolStatsMax :: Int-    , poolStatsAvailable :: Int-    , poolStatsCreated :: Int-    }--poolStats :: Pool a -> IO PoolStats-poolStats (Pool m td _) = do-    d <- atomically $ readTVar td-    return $ PoolStats m (length $ poolAvail d) (poolCreated d)--createPool :: (MonadIO m, I.MonadInvertIO m)-           => IO a -> (a -> IO ()) -> Int -> (Pool a -> m b) -> m b-createPool mk fr mx f = do-    pd <- liftIO $ newTVarIO $ PoolData [] 0-    I.finally (f $ Pool mx pd mk) $ liftIO $ do-        PoolData ress _ <- atomically $ readTVar pd-        mapM_ fr ress--data PoolExhaustedException = PoolExhaustedException-    deriving (Show, Typeable)-instance Exception PoolExhaustedException--withPool' :: (MonadIO m, I.MonadInvertIO m) => Pool a -> (a -> m b) -> m b-withPool' p f = do-    x <- withPool p f-    case x of-        Nothing -> liftIO $ throwIO PoolExhaustedException-        Just x' -> return x'--withPool :: (MonadIO m, I.MonadInvertIO m)-         => Pool a -> (a -> m b) -> m (Maybe b)-withPool p f = I.block $ do-    eres <- liftIO $ atomically $ do-        pd <- readTVar $ poolData p-        let (pd', eres) =-                case poolAvail pd of-                    (x:xs) -> (pd { poolAvail = xs }, Right x)-                    [] -> (pd, Left $ poolCreated pd)-        writeTVar (poolData p) pd'-        return eres-    case eres of-        Left pc ->-            if pc >= poolMax p-                then return Nothing-                else I.bracket-                    (liftIO $ poolMake p)-                    (insertResource 1)-                    (liftM Just . I.unblock . f)-        Right res -> I.finally-                        (liftM Just $ I.unblock $ f res)-                        (insertResource 0 res)-  where-    insertResource i x = liftIO $ atomically $ do-        pd <- readTVar $ poolData p-        writeTVar (poolData p)-            pd { poolAvail = x : poolAvail pd-               , poolCreated = i + poolCreated pd-               }
Database/Persist/Quasi.hs view
@@ -1,4 +1,7 @@-module Database.Persist.Quasi (persist) where+module Database.Persist.Quasi+    ( persist+    , persistFile+    ) where  import Language.Haskell.TH.Quote import Language.Haskell.TH.Syntax@@ -6,6 +9,7 @@ import Data.Char import Data.Maybe (mapMaybe) import Text.ParserCombinators.Parsec hiding (token)+import qualified System.IO as SIO  -- | Converts a quasi-quoted syntax into a list of entity definitions, to be -- used as input to the template haskell generation code (mkPersist).@@ -13,6 +17,13 @@ persist = QuasiQuoter     { quoteExp = lift . parse_     }++persistFile :: FilePath -> Q Exp+persistFile fp = do+    h <- qRunIO $ SIO.openFile fp SIO.ReadMode+    qRunIO $ SIO.hSetEncoding h SIO.utf8_bom+    s <- qRunIO $ SIO.hGetContents h+    lift $ parse_ s  parse_ :: String -> [EntityDef] parse_ = map parse' . nest . map words'
persistent.cabal view
@@ -1,31 +1,31 @@ name:            persistent-version:         0.3.1.3+version:         0.4.0 license:         BSD3 license-file:    LICENSE author:          Michael Snoyman <michael@snoyman.com> maintainer:      Michael Snoyman <michael@snoyman.com> synopsis:        Type-safe, non-relational, multi-backend persistence. description:     This library provides just the general interface and helper functions. You must use a specific backend in order to make this useful.-category:        Database+category:        Database, Yesod stability:       Stable cabal-version:   >= 1.6 build-type:      Simple homepage:        http://docs.yesodweb.com/persistent/  library-    build-depends:   base >= 4 && < 5,-                     template-haskell >= 2.4 && < 2.6,-                     bytestring >= 0.9.1 && < 0.10,-                     transformers >= 0.2.1 && < 0.3,-                     time >= 1.1.4 && < 1.3,-                     text >= 0.7.1 && < 0.12,-                     hamlet >= 0.5 && < 0.7,-                     web-routes-quasi >= 0.6.0 && < 0.7,-                     containers >= 0.2 && < 0.5,-                     parsec >= 2.1 && < 4,-                     enumerator >= 0.4 && < 0.5,-                     stm >= 2.1 && < 2.3,-                     neither >= 0.1 && < 0.2+    build-depends:   base                     >= 4         && < 5+                   , template-haskell+                   , bytestring               >= 0.9       && < 0.10+                   , transformers             >= 0.2.1     && < 0.3+                   , time                     >= 1.1.4     && < 1.3+                   , text                     >= 0.8       && < 0.12+                   , web-routes-quasi         >= 0.6       && < 0.7+                   , containers               >= 0.2       && < 0.5+                   , parsec                   >= 2.1       && < 4+                   , enumerator               >= 0.4       && < 0.5+                   , monad-peel               >= 0.1       && < 0.2+                   , pool                     >= 0.0       && < 0.1+                   , blaze-html               >= 0.4       && < 0.5     exposed-modules: Database.Persist                      Database.Persist.Base                      Database.Persist.TH@@ -33,8 +33,41 @@                      Database.Persist.GenericSql                      Database.Persist.GenericSql.Internal                      Database.Persist.GenericSql.Raw-                     Database.Persist.Pool     ghc-options:     -Wall++Flag test+    Description:   Build the runtests executables.+    Default:       False++executable         runtests+    if flag(test)+        Buildable: True+        build-depends:   haskell98,+                         HUnit,+                         test-framework,+                         test-framework-hunit,+                         base >= 4 && < 5,+                         template-haskell >= 2.4 && < 2.6,+                         bytestring >= 0.9.1 && < 0.10,+                         transformers >= 0.2.1 && < 0.3,+                         time >= 1.1.4 && < 1.3,+                         text >= 0.7.1 && < 0.12,+                         hamlet >= 0.5 && < 0.7,+                         web-routes-quasi >= 0.6.0 && < 0.7,+                         containers >= 0.2 && < 0.5,+                         parsec >= 2.1 && < 4,+                         enumerator >= 0.4 && < 0.5,+                         stm >= 2.1 && < 2.3,+                         neither >= 0.1 && < 0.2,+                         HDBC-postgresql,+                         HDBC,+                         utf8-string+    else+        Buildable: False+    hs-source-dirs: ., backends/sqlite, backends/postgresql+    main-is:       runtests.hs+    ghc-options:   -Wall+    extra-libraries: sqlite3  source-repository head   type:     git
+ runtests.hs view
@@ -0,0 +1,345 @@+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE NoMonomorphismRestriction #-}
+
+import Test.Framework (defaultMain, testGroup, Test)
+import Test.Framework.Providers.HUnit
+-- import Test.Framework.Providers.QuickCheck
+import Test.HUnit hiding (Test)
+
+import Database.Persist.Sqlite
+import Database.Persist.Postgresql
+import Database.Persist.TH
+import Control.Monad.IO.Class
+
+import Control.Monad.Trans.Reader
+import Monad (unless)
+import Data.Int
+import Data.Word
+
+import Control.Exception (SomeException)
+import qualified Control.Exception.Peel as Peel
+
+infix 1 /=@, @/=
+
+(/=@) :: (Eq a, Show a, MonadIO m) => a -> a -> m ()
+expected /=@ actual = liftIO $ assertNotEqual "" expected actual
+
+(@/=) :: (Eq a, Show a, MonadIO m) => a -> a -> m ()
+actual @/= expected = liftIO $ assertNotEqual "" expected actual
+
+assertNotEqual :: (Eq a, Show a) => String -> a -> a -> Assertion
+assertNotEqual preface expected actual =
+  unless (actual /= expected) (assertFailure msg)
+  where msg = (if null preface then "" else preface ++ "\n") ++
+             "expected: " ++ show expected ++ "\n to not equal: " ++ show actual
+
+infix 1 @==, ==@
+expected @== actual = liftIO $ expected @?= actual
+expected ==@ actual = liftIO $ expected @=? actual
+
+data PetType = Cat | Dog
+    deriving (Show, Read, Eq)
+derivePersistField "PetType"
+
+  -- FIXME Empty
+share2 mkPersist (mkMigrate "testMigrate") [$persist|
+
+  Person
+    name String update Eq Ne Desc
+    age Int update "Asc" Desc Lt "some ignored -- attribute" Eq Add
+    color String Maybe Eq Ne -- this is a comment sql=foobarbaz
+    PersonNameKey name -- this is a comment sql=foobarbaz
+  Pet
+    owner PersonId
+    name String
+    type PetType
+  NeedsPet
+    pet PetId
+  Number
+    int Int
+    int32 Int32
+    word32 Word32
+    int64 Int64
+    word64 Word64
+|]
+
+-- connstr = "user=test password=test host=localhost port=5432 dbname=yesod_test"
+
+runConn f = do
+    (withSqlitePool "testdb" 1) $ runSqlPool f
+    (withPostgresqlPool "user=test password=test host=localhost port=5432 dbname=test" 1) $ runSqlPool f
+
+-- TODO: run tests in transaction
+sqliteTest :: SqlPersist IO () -> Assertion
+sqliteTest actions = do
+  runConn actions
+  runConn cleanDB
+
+cleanDB :: SqlPersist IO ()
+cleanDB = do
+  deleteWhere ([] :: [Filter Pet])
+  deleteWhere ([] :: [Filter Person])
+  deleteWhere ([] :: [Filter Number])
+
+setup :: SqlPersist IO ()
+setup = do
+  runMigration testMigrate
+  cleanDB
+
+main :: IO ()
+main = do
+  runConn setup
+  defaultMain [testSuite]
+
+testSuite :: Test
+testSuite = testGroup "Database.Persistent"
+    [
+      testCase "sqlite persistent" case_sqlitePersistent
+    , testCase "sqlite deleteWhere" case_sqliteDeleteWhere
+    , testCase "sqlite deleteBy" case_sqliteDeleteBy
+    , testCase "sqlite delete" case_sqliteDelete
+    , testCase "sqlite replace" case_sqliteReplace
+    , testCase "sqlite getBy" case_sqliteGetBy
+    , testCase "sqlite update" case_sqliteUpdate
+    , testCase "sqlite updateWhere" case_sqliteUpdateWhere
+    , testCase "sqlite selectList" case_sqliteSelectList
+    , testCase "large numbers" case_largeNumbers
+    , testCase "insertBy" case_insertBy
+    , testCase "derivePersistField" case_derivePersistField
+    , testCase "afterException" case_afterException
+    ]
+
+                          
+assertEmpty xs    = liftIO $ assertBool "" (null xs)
+assertNotEmpty xs = liftIO $ assertBool "" (not (null xs))
+
+case_sqliteDeleteWhere = sqliteTest _deleteWhere
+case_sqliteDeleteBy = sqliteTest _deleteBy
+case_sqliteDelete = sqliteTest _delete
+case_sqliteReplace = sqliteTest _replace
+case_sqliteGetBy = sqliteTest _getBy
+case_sqliteUpdate = sqliteTest _update
+case_sqliteUpdateWhere = sqliteTest _updateWhere
+case_sqliteSelectList = sqliteTest _selectList
+case_sqlitePersistent = sqliteTest _persistent
+case_largeNumbers = sqliteTest _largeNumbers
+case_insertBy = sqliteTest _insertBy
+case_derivePersistField = sqliteTest _derivePersistField
+case_afterException = sqliteTest _afterException
+
+_deleteWhere = do
+  key2 <- insert $ Person "Michael2" 90 Nothing
+  _    <- insert $ Person "Michael3" 90 Nothing
+  let p91 = Person "Michael4" 91 Nothing
+  key91 <- insert $ p91
+
+  ps90 <- selectList [PersonAgeEq 90] [] 0 0
+  assertNotEmpty ps90
+  deleteWhere [PersonAgeEq 90]
+  ps90 <- selectList [PersonAgeEq 90] [] 0 0
+  assertEmpty ps90
+  Nothing <- get key2
+
+  Just p2_91 <- get key91
+  p91 @== p2_91
+
+_deleteBy = do
+  key2 <- insert $ Person "Michael2" 27 Nothing
+  let p3 = Person "Michael3" 27 Nothing
+  key3 <- insert $ p3
+
+  ps2 <- selectList [PersonNameEq "Michael2"] [] 0 0
+  assertNotEmpty ps2
+
+  deleteBy $ PersonNameKey "Michael2"
+  ps2 <- selectList [PersonNameEq "Michael2"] [] 0 0
+  assertEmpty ps2
+
+  Just p32 <- get key3
+  p3 @== p32
+
+_delete = do
+  key2 <- insert $ Person "Michael2" 27 Nothing
+  let p3 = Person "Michael3" 27 Nothing
+  key3 <- insert $ p3
+
+  pm2 <- selectList [PersonNameEq "Michael2"] [] 0 0
+  assertNotEmpty pm2
+  delete key2
+  pm2 <- selectList [PersonNameEq "Michael2"] [] 0 0
+  assertEmpty pm2
+
+  Just p <- get key3
+  p3 @== p
+
+-- also a decent test of get
+_replace = do
+  key2 <- insert $ Person "Michael2" 27 Nothing
+  let p3 = Person "Michael3" 27 Nothing
+  replace key2 p3
+  Just p <- get key2
+  p @== p3
+
+  -- test replace an empty key
+  delete key2
+  Nothing <- get key2
+  undefined <- replace key2 p3
+  Nothing <- get key2
+  return ()
+
+_getBy = do
+  let p2 = Person "Michael2" 27 Nothing
+  key2 <- insert p2
+  Just (k, p) <- getBy $ PersonNameKey "Michael2"
+  p @== p2
+  k @== key2
+  Nothing <- getBy $ PersonNameKey "Michael3"
+  return ()
+
+_update = do
+  let p25 = Person "Michael" 25 Nothing
+  key25 <- insert p25
+  update key25 [PersonAge 28, PersonName "Updated"]
+  Just pBlue28 <- get key25
+  pBlue28 @== Person "Updated" 28 Nothing
+  update key25 [PersonAgeAdd 2]
+  Just pBlue30 <- get key25
+  pBlue30 @== Person "Updated" 30 Nothing
+
+_updateWhere = do
+  let p1 = Person "Michael" 25 Nothing
+  let p2 = Person "Michael2" 25 Nothing
+  key1 <- insert p1
+  key2 <- insert p2
+  updateWhere [PersonNameEq "Michael2"]
+              [PersonAgeAdd 3, PersonName "Updated"]
+  Just pBlue28 <- get key2
+  pBlue28 @== Person "Updated" 28 Nothing
+  Just p <- get key1
+  p @== p1
+
+_selectList = do
+  let p25 = Person "Michael" 25 Nothing
+  let p26 = Person "Michael2" 26 Nothing
+  key25 <- insert p25
+  key26 <- insert p26
+  ps <- selectList [] [] 0 0
+  ps @== [(key25, p25), (key26, p26)]
+  -- limit
+  ps <- selectList [] [] 1 0
+  ps @== [(key25, p25)]
+  -- offset -- FAILS!
+  ps <- selectList [] [] 0 1
+  ps @== [(key26, p26)]
+  -- limit & offset
+  ps <- selectList [] [] 1 1
+  ps @== [(key26, p26)]
+
+  ps <- selectList [] [PersonAgeDesc] 0 0
+  ps @== [(key26, p26), (key25, p25)]
+  ps <- selectList [PersonAgeEq 26] [] 0 0
+  ps @== [(key26, p26)]
+
+-- general tests transferred from already exising test file
+_persistent = do
+  let mic = Person "Michael" 25 Nothing
+  micK <- insert mic
+  Just p <- get micK
+  p @== mic
+
+  replace micK $ Person "Michael" 25 Nothing
+  Just p <- get micK
+  p @== mic
+
+  replace micK $ Person "Michael" 26 Nothing
+  Just mic26 <- get micK
+  mic26 @/= mic
+  personAge mic26 @== (personAge mic) + 1
+
+  results <- selectList [PersonNameEq "Michael"] [] 0 0
+  results @== [(micK, mic26)]
+
+  results <- selectList [PersonAgeLt 28] [] 0 0
+  results @== [(micK, mic26)]
+
+  update micK [PersonAge 28]
+  Just p28 <- get micK
+  personAge p28 @== 28
+
+  updateWhere [PersonNameEq "Michael"] [PersonAge 29]
+  Just mic29 <- get micK
+  personAge mic29 @== 29
+
+  let eli = Person "Eliezer" 2 $ Just "blue"
+  _ <- insert eli
+  pasc <- selectList [] [PersonAgeAsc] 0 0
+  (map snd pasc) @== [eli, mic29]
+
+  let abe30 = Person "Abe" 30 $ Just "black"
+  keyAbe30 <- insert abe30
+  pdesc <- selectList [PersonAgeLt 30] [PersonNameDesc] 0 0
+  (map snd pasc) @== [eli, mic29]
+
+  abes <- selectList [PersonNameEq "Abe"] [] 0 0
+  (map snd abes) @== [abe30]
+
+  Just (k,p) <- getBy $ PersonNameKey "Michael"
+  p @== mic29
+
+  ps <- selectList [PersonColorEq $ Just "blue"] [] 0 0
+  map snd ps @== [eli]
+
+  ps <- selectList [PersonColorEq Nothing] [] 0 0
+  map snd ps @== [mic29]
+
+  ps <- selectList [PersonColorNe Nothing] [] 0 0
+  map snd ps @== [eli, abe30]
+
+  delete micK
+  Nothing <- get micK
+  return ()
+
+_largeNumbers = do
+    go $ Number maxBound 0 0 0 0
+    go $ Number 0 maxBound 0 0 0
+    go $ Number 0 0 maxBound 0 0
+    go $ Number 0 0 0 maxBound 0
+    go $ Number 0 0 0 0 maxBound
+
+    go $ Number minBound 0 0 0 0
+    go $ Number 0 minBound 0 0 0
+    go $ Number 0 0 minBound 0 0
+    go $ Number 0 0 0 minBound 0
+    go $ Number 0 0 0 0 minBound
+  where
+    go x = do
+        xid <- insert x
+        x' <- get xid
+        liftIO $ x' @?= Just x
+
+_insertBy = do
+    Right _ <- insertBy $ Person "name" 1 Nothing
+    Left _ <- insertBy $ Person "name" 1 Nothing
+    Right _ <- insertBy $ Person "name2" 1 Nothing
+    return ()
+
+_derivePersistField = do
+    person <- insert $ Person "pet owner" 30 Nothing
+    cat <- insert $ Pet person "Mittens" Cat
+    Just cat' <- get cat
+    liftIO $ petType cat' @?= Cat
+    dog <- insert $ Pet person "Spike" Dog
+    Just dog' <- get dog
+    liftIO $ petType dog' @?= Dog
+
+_afterException = do
+    _ <- insert $ Person "A" 0 Nothing
+    _ <- (insert (Person "A" 1 Nothing) >> return ()) `Peel.catch` catcher
+    _ <- insert $ Person "B" 0 Nothing
+    return ()
+  where
+    catcher :: Monad m => SomeException -> m ()
+    catcher _ = return ()