Skip to content

Commit 9ffbc8e

Browse files
committed
Merge pull request #1 from anttih/use-aff
Use purescript-aff
2 parents 0d783d0 + f465802 commit 9ffbc8e

File tree

5 files changed

+242
-157
lines changed

5 files changed

+242
-157
lines changed

MODULE.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Module Documentation
2+
3+
## Module Database.Postgres
4+
5+
#### `Query`
6+
7+
``` purescript
8+
newtype Query a
9+
= Query String
10+
```
11+
12+
13+
#### `Client`
14+
15+
``` purescript
16+
data Client :: *
17+
```
18+
19+
20+
#### `DB`
21+
22+
``` purescript
23+
data DB :: !
24+
```
25+
26+
27+
#### `ConnectionInfo`
28+
29+
``` purescript
30+
type ConnectionInfo = { password :: String, user :: String, port :: Number, db :: String, host :: String }
31+
```
32+
33+
34+
#### `connect`
35+
36+
``` purescript
37+
connect :: forall eff. ConnectionInfo -> Aff (db :: DB | eff) Client
38+
```
39+
40+
Makes a connection to the database
41+
42+
#### `execute`
43+
44+
``` purescript
45+
execute :: forall eff a. Query a -> Client -> Aff (db :: DB | eff) Unit
46+
```
47+
48+
Runs a query and returns nothing
49+
50+
#### `query`
51+
52+
``` purescript
53+
query :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) [a]
54+
```
55+
56+
Runs a query and returns all results
57+
58+
#### `queryOne`
59+
60+
``` purescript
61+
queryOne :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) (Maybe a)
62+
```
63+
64+
Runs a query and returns the first row, if any
65+
66+
#### `queryValue`
67+
68+
``` purescript
69+
queryValue :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) (Maybe a)
70+
```
71+
72+
Runs a query and returns a single value, if any
73+
74+
#### `withConnection`
75+
76+
``` purescript
77+
withConnection :: forall eff a. ConnectionInfo -> (Client -> Aff (db :: DB | eff) a) -> Aff (db :: DB | eff) a
78+
```
79+
80+
Connects to the database, calls the provided function with the client
81+
and returns the results.
82+
83+
#### `end`
84+
85+
``` purescript
86+
end :: forall eff. Client -> Eff (db :: DB | eff) Unit
87+
```
88+
89+
90+
91+

bower.json

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
{
22
"name": "purescript-node-postgres",
33
"version": "0.0.1",
4-
"moduleType": ["node"],
4+
"moduleType": [
5+
"node"
6+
],
57
"ignore": [
68
"**/.*",
79
"node_modules",
810
"bower_components",
911
"output"
1012
],
1113
"dependencies": {
12-
"purescript-arrays" : "*",
13-
"purescript-either" : "*",
14-
"purescript-foreign" : "*",
15-
"purescript-foldable-traversable" : "*",
16-
"purescript-transformers" : "*"
14+
"purescript-arrays": "*",
15+
"purescript-either": "*",
16+
"purescript-foreign": "*",
17+
"purescript-foldable-traversable": "*",
18+
"purescript-transformers": "*",
19+
"purescript-aff": "0.10.0"
1720
}
1821
}

src/Database/Postgres.purs

Lines changed: 111 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,37 @@
1-
module Database.Postgres where
1+
module Database.Postgres
2+
( Query(..)
3+
, Client()
4+
, DB()
5+
, ConnectionInfo()
6+
, connect
7+
, end
8+
, execute
9+
, query
10+
, queryOne
11+
, queryValue
12+
, withConnection
13+
) where
214

315
import Control.Alt
416
import Control.Monad.Eff
5-
import Control.Monad.Cont.Trans
617
import Control.Monad.Trans
718
import Data.Either
819
import Data.Array
920
import Data.Foreign
1021
import Data.Foreign.Class
11-
import Util (readSingularProperty)
22+
import Data.Maybe
23+
import Control.Monad.Aff
24+
import Control.Monad.Eff.Class
25+
import Control.Monad.Eff.Exception(Error(), error)
26+
import Control.Monad.Error.Class (throwError)
27+
import Data.Traversable (sequence)
1228

1329
newtype Query a = Query String
1430

1531
foreign import data Client :: *
1632

1733
foreign import data DB :: !
1834

19-
type DBEff eff = Eff (db :: DB | eff)
20-
2135
type ConnectionInfo =
2236
{ host :: String
2337
, db :: String
@@ -26,81 +40,107 @@ type ConnectionInfo =
2640
, password :: String
2741
}
2842

29-
30-
-- Low-level API. --------------------------------------------------------------
31-
32-
connect :: forall eff. ConnectionInfo -> DBEff eff Client
33-
connect ci = connectJS $ "postgres://" <> ci.user <> ":" <> ci.password <> "@" <> ci.host <> ":" <> show ci.port <> "/" <> ci.db
34-
35-
runQuery :: forall row eff. (IsForeign row) =>
36-
Query row -> Client -> ([F row] -> DBEff eff Unit) -> DBEff eff Unit
37-
runQuery (Query query) client handleRows = runQueryRowsForeignRaw query client (map deserialize >>> handleRows)
38-
where deserialize foreignVal = read foreignVal <|> readSingularProperty foreignVal
39-
40-
41-
foreign import connectJS """
42-
function connectJS(conString) {
43-
return function() {
43+
-- | Makes a connection to the database.
44+
connect :: forall eff. ConnectionInfo -> Aff (db :: DB | eff) Client
45+
connect ci = connect'
46+
$ "postgres://"
47+
<> ci.user <> ":"
48+
<> ci.password <> "@"
49+
<> ci.host <> ":"
50+
<> show ci.port <> "/"
51+
<> ci.db
52+
53+
-- | Runs a query and returns nothing.
54+
execute :: forall eff a. Query a -> Client -> Aff (db :: DB | eff) Unit
55+
execute (Query sql) client = void $ runQuery sql client
56+
57+
-- | Runs a query and returns all results.
58+
query :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) [a]
59+
query (Query sql) client = do
60+
rows <- runQuery sql client
61+
either liftError pure (sequence $ read <$> rows)
62+
63+
-- | Runs a query and returns the first row, if any
64+
queryOne :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) (Maybe a)
65+
queryOne (Query sql) client = do
66+
rows <- runQuery sql client
67+
maybe (pure Nothing) (either liftError (pure <<< Just)) $ read <$> (rows !! 0)
68+
69+
-- | Runs a query and returns a single value, if any.
70+
queryValue :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) (Maybe a)
71+
queryValue (Query sql) client = do
72+
val <- runQueryValue sql client
73+
either liftError (pure <<< Just) $ read val
74+
75+
-- | Connects to the database, calls the provided function with the client
76+
-- | and returns the results.
77+
withConnection :: forall eff a
78+
. ConnectionInfo
79+
-> (Client -> Aff (db :: DB | eff) a)
80+
-> Aff (db :: DB | eff) a
81+
withConnection info p = do
82+
client <- connect info
83+
finally (p client) $ liftEff (end client)
84+
85+
liftError :: forall e a. ForeignError -> Aff e a
86+
liftError err = throwError $ error (show err)
87+
88+
finally :: forall eff a. Aff eff a -> Aff eff Unit -> Aff eff a
89+
finally a sequel = do
90+
res <- attempt a
91+
sequel
92+
either throwError pure res
93+
94+
foreign import connect' """
95+
function connect$prime(conString) {
96+
return function(success, error) {
4497
var pg = require('pg');
4598
var client = new pg.Client(conString);
99+
client.connect(function(err) {
100+
if (err) {
101+
error(err);
102+
} else {
103+
success(client);
104+
}
105+
})
46106
return client;
107+
}
108+
}
109+
""" :: forall eff. String -> Aff (db :: DB | eff) Client
110+
111+
foreign import runQuery """
112+
function runQuery(queryStr) {
113+
return function(client) {
114+
return function(success, error) {
115+
client.query(queryStr, function(err, result) {
116+
if (err) {
117+
error(err);
118+
} else {
119+
success(result.rows);
120+
}
121+
})
122+
};
47123
};
48124
}
49-
""" :: forall eff. String -> DBEff eff Client
50-
51-
52-
foreign import connectClient """
53-
function connectClient(client) {
54-
return function() {
55-
client.connect();
125+
""" :: forall eff. String -> Client -> Aff (db :: DB | eff) [Foreign]
126+
127+
foreign import runQueryValue """
128+
function runQueryValue(queryStr) {
129+
return function(client) {
130+
return function(success, error) {
131+
client.query(queryStr, function(err, result) {
132+
if (err) return error(err);
133+
success(result.rows.length > 0 ? result.rows[0][result.fields[0].name] : undefined);
134+
})
135+
};
56136
};
57137
}
58-
""" :: forall eff. Client -> DBEff eff Unit
138+
""" :: forall eff. String -> Client -> Aff (db :: DB | eff) Foreign
59139

60-
foreign import endClient """
61-
function endClient(client) {
140+
foreign import end """
141+
function end(client) {
62142
return function() {
63143
client.end();
64144
};
65145
}
66-
""" :: forall eff. Client -> DBEff eff Unit
67-
68-
69-
foreign import runQueryRowsForeignRaw """
70-
function runQueryRowsForeignRaw(queryStr) {
71-
return function (client) {
72-
return function(handleRows) {
73-
return function() {
74-
client.query(queryStr, function(err, result) {
75-
if (err) {
76-
console.error('error running query', err);
77-
} else {
78-
handleRows(result.rows)();
79-
}
80-
});
81-
};
82-
};
83-
};
84-
}
85-
""" :: forall eff eff'. String -> Client -> ([Foreign] -> eff') -> DBEff eff Unit
86-
87-
88-
-- Continuation-based API. -----------------------------------------------------
89-
90-
runQueryCont :: forall eff a. (IsForeign a) => Query a -> Client -> ContT Unit (DBEff eff) [F a]
91-
runQueryCont query client = ContT $ runQuery query client
92-
93-
runQueryCont_ :: forall eff. Query Unit -> Client -> ContT Unit (DBEff eff) [F Unit]
94-
runQueryCont_ query client = ContT $ runQuery query client
95-
96-
withConnectionCont :: forall eff a. ConnectionInfo -> (Client -> ContT Unit (DBEff eff) a) -> ContT Unit (DBEff eff) a
97-
withConnectionCont connectionInfo dbProg = do
98-
client <- lift $ connect connectionInfo
99-
lift $ connectClient client
100-
res <- dbProg client
101-
lift $ endClient client
102-
return res
103-
104-
-- needed for runQuery_
105-
instance unitIsForeign :: IsForeign Unit where
106-
read _ = Right unit
146+
""" :: forall eff. Client -> Eff (db :: DB | eff) Unit

src/Database/Util.purs

Lines changed: 0 additions & 42 deletions
This file was deleted.

0 commit comments

Comments
 (0)