on master help sign in
Minor clean up to simple client API and docs
0c88ce73noch
docs/projectm36_client_library.markdown+3−1
@@ -2,6 +2,8 @@
2
2
3The Project:M36 client library is the preferred method for interacting with Project:M36 databases. It supports all features of the DBMS while other interfaces may not.
3The Project:M36 client library is the preferred method for interacting with Project:M36 databases. It supports all features of the DBMS while other interfaces may not.
4
4
5+**Note:** A less featureful but more convenient client API also is available: see [documentation](simple_api.markdown).
6+
5The client supports both in-process and remote Project:M36 access.
7The client supports both in-process and remote Project:M36 access.
6
8
7## Setup
9## Setup
@@ -10,7 +12,7 @@ The client supports both in-process and remote Project:M36 access.
10```haskell
12```haskell
11import ProjectM36.Client
13import ProjectM36.Client
12```
14```
13−2. Create a Connection with a ConnectionInfo in the Either monad
15+2. Create a Connection with a `ConnectionInfo` in the Either monad
14```haskell
16```haskell
15conn <- connectProjectM36 (InProcessConnectionInfo NoPersistence)
17conn <- connectProjectM36 (InProcessConnectionInfo NoPersistence)
16```
18```
docs/simple_api.markdown+23−22
@@ -2,40 +2,41 @@
2
2
3## Introduction
3## Introduction
4
4
5−For common use cases, the ```ProjectM36.Client.Simple``` API can assist in transaction management. The API is designed to be less overwhelming than the full-featured ```ProjectM36.Client```.
5+For common use cases, the `ProjectM36.Client.Simple` API can assist in transaction management. The API is designed to be less overwhelming than the full-featured `ProjectM36.Client` (see [documentation](projectm36_client_library.markdown)).
6
6
7As such, not all features are available through the simple API, but, because the simple API is a thin layer over the complete API, the user may dip into the complete API at any time.
7As such, not all features are available through the simple API, but, because the simple API is a thin layer over the complete API, the user may dip into the complete API at any time.
8
8
9## Usage
9## Usage
10
10
11−1. Create a ```DbConn``` to connect to the database:
11+ 1. Create a `DbConn` to connect to the database:
12
12
13− ```haskell
13+ ```haskell
14− import ProjectM36.Client.Simple
14+ import ProjectM36.Client.Simple
15− ...
15+ ...
16− eDbconn <- simpleConnectProjectM36 (CrashSafePersistence (tempdir </> "db")) emptyNotificationCallback []
16+ let connInfo = InProcessConnectionInfo (CrashSafePersistence "my.db") emptyNotificationCallback []
17+ eDbconn <- simpleConnectProjectM36 connInfo
17− ```
18+ ```
18
19
19− The result is either a ```DbError``` or the connection linked to the "master" branch of the database.
20+ The result is `Either` a `DbError` or a connection linked to the `"master"` branch of the database.
20
21
21−1. Construct the ```Db``` monad using ```withTransaction```:
22+ 1. Run the `Db` monad using `withTransaction`:
22
23
23− ```haskell
24+ ```haskell
24− withTransaction dbconn $ do
25+ withTransaction dbconn $ do
25− execute $ Assign "x" (ExistingRelation relationTrue)
26+ execute $ Assign "x" (ExistingRelation relationTrue)
26− query $ RelationVariable "x" ()
27+ query $ RelationVariable "x" ()
27− ```
28+ ```
28
29
29− If there is an error in the above update or query, then an exception is thrown behind the scenes which cancels the transactions, rolls back any changes, and the error is returned by ```withTransaction```.
30+ If there is an error in the above update or query, then an exception is thrown behind the scenes which cancels the transactions, rolls back any changes, and the error is returned by `withTransaction`.
30
31
31− ```query``` is used with ```RelationalExpr``` queries which read the current database state.
32+ `query` is used with `RelationalExpr` queries which read the current database state.
32
33
33− ```execute``` is used with ```DatabaseContextExpr``` values which write the current database state.
34+ `execute` is used with `DatabaseContextExpr` values which write the current database state.
34
35
35− To execute a query without cancelling the transaction on an error, use the ```queryOrErr``` and ```executeOrErr``` variants.
36+ To execute a query without cancelling the transaction on an error, use the `queryOrErr` and `executeOrErr` variants.
36
37
37−1. Close the connection:
38+ 1. Close the connection:
38
39
39− ```haskell
40+ ```haskell
40− close dbconn
41+ close dbconn
41− ```
42+ ```
src/lib/ProjectM36/Client/Simple.hs+35−24
@@ -1,13 +1,19 @@
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2module ProjectM36.Client.Simple (
2module ProjectM36.Client.Simple (
3 simpleConnectProjectM36,
3 simpleConnectProjectM36,
4+ simpleConnectProjectM36At,
4 withTransaction,
5 withTransaction,
6+ withTransactionUsing,
5 execute,
7 execute,
8+ executeOrErr,
6 query,
9 query,
10+ queryOrErr,
7 rollback,
11 rollback,
8 close,
12 close,
9 Atom(..),
13 Atom(..),
10 AtomType(..),
14 AtomType(..),
15+ Db,
16+ DbConn,
11 DbError(..),
17 DbError(..),
12 Attribute(..),
18 Attribute(..),
13 C.ConnectionInfo(..),
19 C.ConnectionInfo(..),
@@ -15,62 +21,67 @@ module ProjectM36.Client.Simple (
15 C.NotificationCallback,
21 C.NotificationCallback,
16 C.emptyNotificationCallback,
22 C.emptyNotificationCallback,
17 C.DatabaseContextExpr(..),
23 C.DatabaseContextExpr(..),
18− C.RelationalExprBase(..)
24+ C.RelationalExprBase(..)
19 ) where
25 ) where
20-- | A simplified client interface for Project:M36 database access.
26-- | A simplified client interface for Project:M36 database access.
21−import ProjectM36.Error
27+import Control.Exception.Base
28+import Control.Monad.Reader
22import ProjectM36.Base
29import ProjectM36.Base
23import qualified ProjectM36.Client as C
30import qualified ProjectM36.Client as C
24−import Control.Monad.Reader
31+import ProjectM36.Error
25−import Control.Exception.Base
26
32
27type DbConn = (C.SessionId, C.Connection)
33type DbConn = (C.SessionId, C.Connection)
28
34
29−newtype Db a = Db {runDB :: ReaderT DbConn IO a}
35+newtype Db a = Db {runDb :: ReaderT DbConn IO a}
30 deriving (Functor, Applicative, Monad, MonadIO)
36 deriving (Functor, Applicative, Monad, MonadIO)
31−
37+
32newtype TransactionCancelled = TransactionCancelled DbError deriving Show
38newtype TransactionCancelled = TransactionCancelled DbError deriving Show
33instance Exception TransactionCancelled
39instance Exception TransactionCancelled
34
40
35−-- | A simple alternative to 'connectProjectM36' which includes simple session management
41+-- | A simple alternative to 'connectProjectM36' which includes simple session management.
36−simpleConnectProjectM36 :: C.ConnectionInfo -> IO (Either DbError DbConn)
42+simpleConnectProjectM36At :: HeadName -> C.ConnectionInfo -> IO (Either DbError DbConn)
37−simpleConnectProjectM36 connInfo = do
43+simpleConnectProjectM36At headName connInfo = do
38 eConn <- C.connectProjectM36 connInfo
44 eConn <- C.connectProjectM36 connInfo
39 case eConn of
45 case eConn of
40 Left err -> pure (Left (ConnError err))
46 Left err -> pure (Left (ConnError err))
41 Right conn -> do
47 Right conn -> do
42− eSess <- C.createSessionAtHead conn "master"
48+ eSess <- C.createSessionAtHead conn headName
43 case eSess of
49 case eSess of
44 Left err -> do
50 Left err -> do
45 C.close conn
51 C.close conn
46 pure (Left (RelError err))
52 pure (Left (RelError err))
47 Right sess -> pure (Right (sess, conn))
53 Right sess -> pure (Right (sess, conn))
48−
54+
55+-- | Same as 'simpleConnectProjectM36At' but always connects to the @master@ branch.
56+simpleConnectProjectM36 :: C.ConnectionInfo -> IO (Either DbError DbConn)
57+simpleConnectProjectM36 = simpleConnectProjectM36At "master"
58+
59+-- | Closes the database connection.
49−close :: DbConn -> IO ()
60+close :: DbConn -> IO ()
50close (_ , conn) = C.close conn
61close (_ , conn) = C.close conn
51
62
52-- | Runs a Db monad which may include some database updates. If an exception or error occurs, the transaction is rolled back. Otherwise, the transaction is committed to the head of the current branch.
63-- | Runs a Db monad which may include some database updates. If an exception or error occurs, the transaction is rolled back. Otherwise, the transaction is committed to the head of the current branch.
53withTransaction :: DbConn -> Db a -> IO (Either DbError a)
64withTransaction :: DbConn -> Db a -> IO (Either DbError a)
54−withTransaction sessconn = withTransactionUsing sessconn UnionMergeStrategy
65+withTransaction sessconn = withTransactionUsing sessconn UnionMergeStrategy
55
66
56-- | Same a 'withTransaction' except that the merge strategy can be specified.
67-- | Same a 'withTransaction' except that the merge strategy can be specified.
57withTransactionUsing :: DbConn -> MergeStrategy -> Db a -> IO (Either DbError a)
68withTransactionUsing :: DbConn -> MergeStrategy -> Db a -> IO (Either DbError a)
58withTransactionUsing (sess, conn) strat dbm = do
69withTransactionUsing (sess, conn) strat dbm = do
59 eHeadName <- C.headName sess conn
70 eHeadName <- C.headName sess conn
60− case eHeadName of
71+ case eHeadName of
61 Left err -> pure (Left (RelError err))
72 Left err -> pure (Left (RelError err))
62 Right headName -> do
73 Right headName -> do
63 let successFunc = C.autoMergeToHead sess conn strat headName
74 let successFunc = C.autoMergeToHead sess conn strat headName
64− block = runReaderT (runDB dbm) (sess, conn)
75+ block = runReaderT (runDb dbm) (sess, conn)
65 handler :: TransactionCancelled -> IO (Either DbError a)
76 handler :: TransactionCancelled -> IO (Either DbError a)
66 handler (TransactionCancelled err) = pure (Left err)
77 handler (TransactionCancelled err) = pure (Left err)
67 handle handler $ do
78 handle handler $ do
68 ret <- C.withTransaction sess conn (block >>= pure . Right) successFunc
79 ret <- C.withTransaction sess conn (block >>= pure . Right) successFunc
69− case ret of
80+ case ret of
70− Left err -> pure (Left (RelError err))
81+ Left err -> pure (Left (RelError err))
71 Right val -> pure (Right val)
82 Right val -> pure (Right val)
72
83
73−-- | A union of connection and other errors that can be returned from 'withDBConnection'.
84+-- | A union of connection and other errors that can be returned from 'withTransaction'.
74data DbError = ConnError C.ConnectionError |
85data DbError = ConnError C.ConnectionError |
75 RelError RelationalError |
86 RelError RelationalError |
76 TransactionRolledBack
87 TransactionRolledBack
@@ -82,22 +93,22 @@ execute expr = do
82 ret <- executeOrErr expr
93 ret <- executeOrErr expr
83 case ret of
94 case ret of
84 Left err -> liftIO $ cancelTransaction (RelError err)
95 Left err -> liftIO $ cancelTransaction (RelError err)
85− Right _ -> pure ()
96+ Right _ -> pure ()
86−
97+
87-- | Run a 'RelationalExpr' query in the 'DB' monad. Relational expressions perform read-only queries against the current database state.
98-- | Run a 'RelationalExpr' query in the 'DB' monad. Relational expressions perform read-only queries against the current database state.
88query :: C.RelationalExpr -> Db Relation
99query :: C.RelationalExpr -> Db Relation
89query expr = do
100query expr = do
90 ret <- queryOrErr expr
101 ret <- queryOrErr expr
91 case ret of
102 case ret of
92− Left err -> liftIO $ cancelTransaction (RelError err)
103+ Left err -> liftIO $ cancelTransaction (RelError err)
93 Right rel -> pure rel
104 Right rel -> pure rel
94−
105+
95-- | Run a 'DatabaseContextExpr' update expression. If there is an error, just return it without cancelling the current transaction.
106-- | Run a 'DatabaseContextExpr' update expression. If there is an error, just return it without cancelling the current transaction.
96executeOrErr :: C.DatabaseContextExpr -> Db (Either RelationalError ())
107executeOrErr :: C.DatabaseContextExpr -> Db (Either RelationalError ())
97executeOrErr expr = Db $ do
108executeOrErr expr = Db $ do
98− (sess, conn) <- ask
109+ (sess, conn) <- ask
99 lift $ C.executeDatabaseContextExpr sess conn expr
110 lift $ C.executeDatabaseContextExpr sess conn expr
100−
111+
101-- | Run a 'RelationalExpr' query expression. If there is an error, just return it without cancelling the transaction.
112-- | Run a 'RelationalExpr' query expression. If there is an error, just return it without cancelling the transaction.
102queryOrErr :: C.RelationalExpr -> Db (Either RelationalError Relation)
113queryOrErr :: C.RelationalExpr -> Db (Either RelationalError Relation)
103queryOrErr expr = Db $ do
114queryOrErr expr = Db $ do