@@ -3,6 +3,7 @@ module Database.PostgreSQL.Query.TH
3
3
deriveFromRow
4
4
, deriveToRow
5
5
, deriveEntity
6
+ , deriveEverything
6
7
, EntityOptions (.. )
7
8
-- * Embedding sql files
8
9
, embedSql
@@ -37,31 +38,34 @@ cArgs (NormalC _ n) = return $ length n
37
38
cArgs (RecC _ n) = return $ length n
38
39
cArgs _ = error " Constructor must be simple"
39
40
41
+ -- | Get field names from record constructor
40
42
cFieldNames :: Con -> [Name ]
41
43
cFieldNames (RecC _ vst) = map (\ (a, _, _) -> a) vst
42
44
cFieldNames _ = error " Constructor must be a record (product type with field names)"
43
45
44
- -- | Derive 'FromRow' instance. i.e. you have type like that
45
- --
46
- -- @
47
- -- data Entity = Entity
48
- -- { eField :: Text
49
- -- , eField2 :: Int
50
- -- , efield3 :: Bool }
51
- -- @
52
- --
53
- -- then 'deriveFromRow' will generate this instance:
54
- -- instance FromRow Entity where
55
- --
56
- -- @
57
- -- instance FromRow Entity where
58
- -- fromRow = Entity
59
- -- \<$> field
60
- -- \<*> field
61
- -- \<*> field
62
- -- @
63
- --
64
- -- Datatype must have just one constructor with arbitrary count of fields
46
+ {-| Derive 'FromRow' instance. i.e. you have type like that
47
+
48
+ @
49
+ data Entity = Entity
50
+ { eField :: Text
51
+ , eField2 :: Int
52
+ , efield3 :: Bool }
53
+ @
54
+
55
+ then 'deriveFromRow' will generate this instance:
56
+ instance FromRow Entity where
57
+
58
+ @
59
+ instance FromRow Entity where
60
+ fromRow = Entity
61
+ \<$> field
62
+ \<*> field
63
+ \<*> field
64
+ @
65
+
66
+ Datatype must have just one constructor with arbitrary count of fields
67
+ -}
68
+
65
69
deriveFromRow :: Name -> Q [Dec ]
66
70
deriveFromRow t = do
67
71
TyConI (DataD _ _ _ [con] _) <- reify t
@@ -87,24 +91,26 @@ lookupVNameErr name =
87
91
return
88
92
89
93
90
- -- | derives 'ToRow' instance for datatype like
91
- --
92
- -- @
93
- -- data Entity = Entity
94
- -- { eField :: Text
95
- -- , eField2 :: Int
96
- -- , efield3 :: Bool }
97
- -- @
98
- --
99
- -- it will derive instance like that:
100
- --
101
- -- @
102
- -- instance ToRow Entity where
103
- -- toRow (Entity e1 e2 e3) =
104
- -- [ toField e1
105
- -- , toField e2
106
- -- , toField e3 ]
107
- -- @
94
+ {-| derives 'ToRow' instance for datatype like
95
+
96
+ @
97
+ data Entity = Entity
98
+ { eField :: Text
99
+ , eField2 :: Int
100
+ , efield3 :: Bool }
101
+ @
102
+
103
+ it will derive instance like that:
104
+
105
+ @
106
+ instance ToRow Entity where
107
+ toRow (Entity e1 e2 e3) =
108
+ [ toField e1
109
+ , toField e2
110
+ , toField e3 ]
111
+ @
112
+ -}
113
+
108
114
deriveToRow :: Name -> Q [Dec ]
109
115
deriveToRow t = do
110
116
TyConI (DataD _ _ _ [con] _) <- reify t
@@ -123,6 +129,7 @@ deriveToRow t = do
123
129
(\ e -> AppE (VarE tof) (VarE e))
124
130
v
125
131
132
+ -- | Options for deriving `Entity`
126
133
data EntityOptions = EntityOptions
127
134
{ eoTableName :: String -> String -- ^ Type name to table name converter
128
135
, eoColumnNames :: String -> String -- ^ Record field to column name converter
@@ -138,7 +145,44 @@ instance Default EntityOptions where
138
145
, eoIdType = ''Integer
139
146
}
140
147
141
- -- | Derives instance for 'Entity' using type name and field names.
148
+ {- | Derives instance for 'Entity' using type name and field names. Also
149
+ generates type synonim for ID. E.g. code like this:
150
+
151
+ @
152
+ data Agent = Agent
153
+ { aName :: !Text
154
+ , aAttributes :: !HStoreMap
155
+ , aLongWeirdName :: !Int
156
+ } deriving (Ord, Eq, Show)
157
+
158
+ $(deriveEntity
159
+ def { eoIdType = ''Id
160
+ , eoTableName = toUnderscore
161
+ , eoColumnNames = toUnderscore . drop 1
162
+ , eoDeriveClassess =
163
+ [''Show, ''Read, ''Ord, ''Eq
164
+ , ''FromField, ''ToField, ''PathPiece]
165
+ }
166
+ ''Agent )
167
+ @
168
+
169
+ Will generate code like this:
170
+
171
+ @
172
+ instance Database.PostgreSQL.Query.Entity Agent where
173
+ newtype EntityId Agent
174
+ = AgentId {getAgentId :: Id}
175
+ deriving (Show, Read, Ord, Eq, FromField, ToField, PathPiece)
176
+ tableName _ = "agent"
177
+ fieldNames _ = ["name", "attributes", "long_weird_name"]
178
+ type AgentId = EntityId Agent
179
+ @
180
+
181
+ So, you dont need to write it by hands any more.
182
+
183
+ NOTE: 'toUnderscore' is from package 'inflections' here
184
+ -}
185
+
142
186
deriveEntity :: EntityOptions -> Name -> Q [Dec ]
143
187
deriveEntity opts tname = do
144
188
TyConI (DataD _ _ _ [tcon] _) <- reify tname
@@ -154,16 +198,62 @@ deriveEntity opts tname = do
154
198
idcon (eoDeriveClassess opts)
155
199
tblName = eoTableName opts tnames
156
200
fldNames = map (eoColumnNames opts . nameBase) $ cFieldNames tcon
157
- VarE tableName <- [e |tableName|]
158
- VarE fieldNames <- [e |fieldNames|]
159
- let tbldec = FunD tableName [Clause [WildP ] (NormalB $ LitE $ stringL tblName) [] ]
160
- flddec = FunD fieldNames [Clause [WildP ] (NormalB $ ListE $ map (LitE . stringL) fldNames) [] ]
201
+ VarE ntableName <- [e |tableName|]
202
+ VarE nfieldNames <- [e |fieldNames|]
203
+ let tbldec = FunD ntableName [Clause [WildP ] (NormalB $ LitE $ stringL tblName) [] ]
204
+ flddec = FunD nfieldNames [Clause [WildP ] (NormalB $ ListE $ map (LitE . stringL) fldNames) [] ]
161
205
ret = InstanceD [] econt
162
206
[ iddec, tbldec, flddec ]
163
207
syndec = TySynD (mkName idname) [] (AppT (ConT entityIdName) (ConT tname))
164
208
return [ret, syndec]
165
209
210
+ {- | Calls sequently `deriveFromRow` `deriveToRow` `deriveEntity`. E.g. code like this:
211
+
212
+ @
213
+ data Agent = Agent
214
+ { aName :: !Text
215
+ , aAttributes :: !HStoreMap
216
+ , aLongWeirdName :: !Int
217
+ } deriving (Ord, Eq, Show)
218
+
219
+ $(deriveEverything
220
+ def { eoIdType = ''Id
221
+ , eoTableName = toUnderscore
222
+ , eoColumnNames = toUnderscore . drop 1
223
+ , eoDeriveClassess =
224
+ [''Show, ''Read, ''Ord, ''Eq
225
+ , ''FromField, ''ToField, ''PathPiece]
226
+ }
227
+ ''Agent )
228
+ @
229
+
230
+ will generate that:
231
+
232
+ @
233
+ instance ToRow Agent where
234
+ toRow (Agent a_aE3w a_aE3x a_aE3y)
235
+ = [toField a_aE3w, toField a_aE3x, toField a_aE3y]
236
+ instance FromRow Agent where
237
+ fromRow
238
+ = Agent <$> Database.PostgreSQL.Simple.FromRow.field
239
+ <*> Database.PostgreSQL.Simple.FromRow.field
240
+ <*> Database.PostgreSQL.Simple.FromRow.field
241
+ instance Database.PostgreSQL.Query.Entity Agent where
242
+ newtype EntityId Agent
243
+ = AgentId {getAgentId :: Id}
244
+ deriving (Show, Read, Ord, Eq, FromField, ToField, PathPiece)
245
+ tableName _ = "agent"
246
+ fieldNames _ = ["name", "attributes", "long_weird_name"]
247
+ type AgentId = EntityId Agent
248
+ @
249
+
250
+ -}
166
251
252
+ deriveEverything :: EntityOptions -> Name -> Q [Dec ]
253
+ deriveEverything opts tname = fmap concat $ sequence
254
+ [ deriveToRow tname
255
+ , deriveFromRow tname
256
+ , deriveEntity opts tname ]
167
257
168
258
-- embed sql file as value
169
259
embedSql :: String -- ^ File path
0 commit comments