Skip to content

Commit 8751a67

Browse files
committed
add deriveEverything + docs + codestyle
1 parent 1c864d6 commit 8751a67

File tree

1 file changed

+134
-44
lines changed
  • src/Database/PostgreSQL/Query

1 file changed

+134
-44
lines changed

src/Database/PostgreSQL/Query/TH.hs

Lines changed: 134 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module Database.PostgreSQL.Query.TH
33
deriveFromRow
44
, deriveToRow
55
, deriveEntity
6+
, deriveEverything
67
, EntityOptions(..)
78
-- * Embedding sql files
89
, embedSql
@@ -37,31 +38,34 @@ cArgs (NormalC _ n) = return $ length n
3738
cArgs (RecC _ n) = return $ length n
3839
cArgs _ = error "Constructor must be simple"
3940

41+
-- | Get field names from record constructor
4042
cFieldNames :: Con -> [Name]
4143
cFieldNames (RecC _ vst) = map (\(a, _, _) -> a) vst
4244
cFieldNames _ = error "Constructor must be a record (product type with field names)"
4345

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+
6569
deriveFromRow :: Name -> Q [Dec]
6670
deriveFromRow t = do
6771
TyConI (DataD _ _ _ [con] _) <- reify t
@@ -87,24 +91,26 @@ lookupVNameErr name =
8791
return
8892

8993

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+
108114
deriveToRow :: Name -> Q [Dec]
109115
deriveToRow t = do
110116
TyConI (DataD _ _ _ [con] _) <- reify t
@@ -123,6 +129,7 @@ deriveToRow t = do
123129
(\e -> AppE (VarE tof) (VarE e))
124130
v
125131

132+
-- | Options for deriving `Entity`
126133
data EntityOptions = EntityOptions
127134
{ eoTableName :: String -> String -- ^ Type name to table name converter
128135
, eoColumnNames :: String -> String -- ^ Record field to column name converter
@@ -138,7 +145,44 @@ instance Default EntityOptions where
138145
, eoIdType = ''Integer
139146
}
140147

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+
142186
deriveEntity :: EntityOptions -> Name -> Q [Dec]
143187
deriveEntity opts tname = do
144188
TyConI (DataD _ _ _ [tcon] _) <- reify tname
@@ -154,16 +198,62 @@ deriveEntity opts tname = do
154198
idcon (eoDeriveClassess opts)
155199
tblName = eoTableName opts tnames
156200
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) []]
161205
ret = InstanceD [] econt
162206
[ iddec, tbldec, flddec ]
163207
syndec = TySynD (mkName idname) [] (AppT (ConT entityIdName) (ConT tname))
164208
return [ret, syndec]
165209

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+
-}
166251

252+
deriveEverything :: EntityOptions -> Name -> Q [Dec]
253+
deriveEverything opts tname = fmap concat $ sequence
254+
[ deriveToRow tname
255+
, deriveFromRow tname
256+
, deriveEntity opts tname ]
167257

168258
-- embed sql file as value
169259
embedSql :: String -- ^ File path

0 commit comments

Comments
 (0)