persistent-template 1.1.3.1 → 1.2.0
raw patch · 3 files changed
+190/−137 lines, 3 filesdep +monad-loggerdep ~persistentPVP ok
version bump matches the API change (PVP)
Dependencies added: monad-logger
Dependency ranges changed: persistent
API changes (from Hackage documentation)
- Database.Persist.TH: instance Lift EntityDef
- Database.Persist.TH: instance Lift FieldDef
- Database.Persist.TH: persist :: QuasiQuoter
- Database.Persist.TH: persistFile :: FilePath -> Q Exp
- Database.Persist.TH: share2 :: ([EntityDef] -> Q [Dec]) -> ([EntityDef] -> Q [Dec]) -> [EntityDef] -> Q [Dec]
+ Database.Persist.TH: instance Lift SqlType
+ Database.Persist.TH: instance Lift SqlTypeExp
+ Database.Persist.TH: instance Lift' ()
+ Database.Persist.TH: instance Lift' SqlType
+ Database.Persist.TH: instance Lift' SqlTypeExp
+ Database.Persist.TH: instance Lift' a => Lift (EntityDef a)
+ Database.Persist.TH: instance Lift' a => Lift (FieldDef a)
+ Database.Persist.TH: instance Lift' a => Lift' (EntityDef a)
+ Database.Persist.TH: instance Lift' a => Lift' (Maybe a)
- Database.Persist.TH: mkDeleteCascade :: MkPersistSettings -> [EntityDef] -> Q [Dec]
+ Database.Persist.TH: mkDeleteCascade :: MkPersistSettings -> [EntityDef a] -> Q [Dec]
- Database.Persist.TH: mkMigrate :: String -> [EntityDef] -> Q [Dec]
+ Database.Persist.TH: mkMigrate :: Lift' a => String -> [EntityDef a] -> Q [Dec]
- Database.Persist.TH: mkPersist :: MkPersistSettings -> [EntityDef] -> Q [Dec]
+ Database.Persist.TH: mkPersist :: MkPersistSettings -> [EntityDef SqlType] -> Q [Dec]
- Database.Persist.TH: mkSave :: String -> [EntityDef] -> Q [Dec]
+ Database.Persist.TH: mkSave :: String -> [EntityDef SqlType] -> Q [Dec]
- Database.Persist.TH: persistFieldFromEntity :: MkPersistSettings -> EntityDef -> Q [Dec]
+ Database.Persist.TH: persistFieldFromEntity :: MkPersistSettings -> EntityDef a -> Q [Dec]
- Database.Persist.TH: share :: [[EntityDef] -> Q [Dec]] -> [EntityDef] -> Q [Dec]
+ Database.Persist.TH: share :: [[EntityDef a] -> Q [Dec]] -> [EntityDef a] -> Q [Dec]
Files
- Database/Persist/TH.hs +187/−134
- persistent-template.cabal +3/−2
- test/main.hs +0/−1
Database/Persist/TH.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-}@@ -11,9 +12,6 @@ , persistUpperCase , persistLowerCase , persistFileWith- -- ** Deprecated synonyms- , persist- , persistFile -- * Turn @EntityDef@s into types , mkPersist , MkPersistSettings@@ -29,18 +27,12 @@ , share , derivePersistField , persistFieldFromEntity- -- ** Deprecated- , share2 ) where import Prelude hiding ((++), take, concat, splitAt)-import Database.Persist.EntityDef+import Database.Persist+import Database.Persist.Sql (Migration, SqlPersistT, migrate, SqlBackend, PersistFieldSql) import Database.Persist.Quasi-import Database.Persist.Store-import Database.Persist.Query.Internal-import Database.Persist.GenericSql (Migration, SqlPersist, migrate)-import Database.Persist.GenericSql.Raw (SqlBackend)-import Database.Persist.Util (nullable, IsNullable(..), WhyNullable(..)) import Language.Haskell.TH.Quote import Language.Haskell.TH.Syntax import Data.Char (toLower, toUpper)@@ -50,7 +42,8 @@ import qualified System.IO as SIO import Data.Text (pack, Text, append, unpack, concat, uncons, cons) import qualified Data.Text.IO as TIO-import Data.List (foldl')+import Data.List (foldl', find)+import Data.Maybe (isJust) import Data.Monoid (mappend, mconcat) import qualified Data.Map as M import Data.Aeson@@ -58,19 +51,16 @@ , Value (Object), (.:), (.:?) ) import Control.Applicative (pure, (<*>))+import Control.Monad.Logger (MonadLogger)+import Database.Persist.Sql (sqlType) -- | Converts a quasi-quoted syntax into a list of entity definitions, to be -- used as input to the template haskell generation code (mkPersist). persistWith :: PersistSettings -> QuasiQuoter persistWith ps = QuasiQuoter- { quoteExp = lift . parse ps . pack+ { quoteExp = parseSqlType ps . pack } --- | Deprecate synonym for 'persistUpperCase'.-persist :: QuasiQuoter-persist = persistUpperCase-{-# DEPRECATED persist "Please use persistUpperCase instead." #-}- -- | Apply 'persistWith' to 'upperCaseSettings'. persistUpperCase :: QuasiQuoter persistUpperCase = persistWith upperCaseSettings@@ -89,15 +79,61 @@ h <- qRunIO $ SIO.openFile fp SIO.ReadMode qRunIO $ SIO.hSetEncoding h SIO.utf8_bom s <- qRunIO $ TIO.hGetContents h- lift $ parse ps s+ parseSqlType ps s --- | Deprecated function. Equivalent to @persistFileWith upperCaseSettings@.-persistFile :: FilePath -> Q Exp-persistFile = persistFileWith upperCaseSettings+parseSqlType :: PersistSettings -> Text -> Q Exp+parseSqlType ps s =+ lift $ map (getSqlType defsOrig) defsOrig+ where+ defsOrig = parse ps s +getSqlType :: [EntityDef ()] -> EntityDef () -> EntityDef SqlTypeExp+getSqlType allEntities ent =+ ent+ { entityFields = map go $ entityFields ent+ }+ where+ go :: FieldDef () -> FieldDef SqlTypeExp+ go field = do+ field+ { fieldSqlType = final+ , fieldEmbedded = mEmbedded (fieldType field)+ }+ where+ -- In the case of embedding, there won't be any datatype created yet.+ -- We just use SqlString, as the data will be serialized to JSON.+ final+ | isJust (mEmbedded (fieldType field)) = SqlString'+ | isReference = SqlInt64'+ | otherwise = SqlTypeExp st++ mEmbedded (FTTypeCon Just{} _) = Nothing+ mEmbedded (FTTypeCon Nothing n) = let name = HaskellName n in+ find ((name ==) . entityHaskell) allEntities + mEmbedded (FTList x) = mEmbedded x+ mEmbedded (FTApp x y) = maybe (mEmbedded y) Just (mEmbedded x)++ isReference =+ case stripId $ fieldType field of+ Just{} -> True+ Nothing -> False++ typ = ftToType $ fieldType field+ mtyp = (ConT ''Maybe `AppT` typ)+ typedNothing = SigE (ConE 'Nothing) mtyp+ st = VarE 'sqlType `AppE` typedNothing++data SqlTypeExp = SqlTypeExp Exp+ | SqlString'+ | SqlInt64'+instance Lift SqlTypeExp where+ lift (SqlTypeExp e) = return e+ lift SqlString' = [|SqlString|]+ lift SqlInt64' = [|SqlInt64|]+ -- | Create data types and appropriate 'PersistEntity' instances for the given -- 'EntityDef's. Works well with the persist quasi-quoter.-mkPersist :: MkPersistSettings -> [EntityDef] -> Q [Dec]+mkPersist :: MkPersistSettings -> [EntityDef SqlType] -> Q [Dec] mkPersist mps ents' = do x <- fmap mconcat $ mapM (persistFieldFromEntity mps) ents y <- fmap mconcat $ mapM (mkEntity mps) ents@@ -108,7 +144,7 @@ -- | Implement special preprocessing on EntityDef as necessary for 'mkPersist'. -- For example, strip out any fields marked as MigrationOnly.-fixEntityDef :: EntityDef -> EntityDef+fixEntityDef :: EntityDef a -> EntityDef a fixEntityDef ed = ed { entityFields = filter keepField $ entityFields ed } where@@ -161,15 +197,15 @@ Just (a, b) -> cons (toUpper a) b Nothing -> t -dataTypeDec :: MkPersistSettings -> EntityDef -> Dec+dataTypeDec :: MkPersistSettings -> EntityDef a -> Dec dataTypeDec mps t = DataD [] nameFinal paramsFinal constrs $ map (mkName . unpack) $ entityDerives t where- mkCol x (FieldDef n _ ty as) =- (mkName $ unpack $ recName x $ unHaskellName n,- NotStrict,- pairToType mps backend (ty, nullable as)+ mkCol x FieldDef {..} =+ (mkName $ unpack $ recName x $ unHaskellName fieldHaskell,+ if fieldStrict then IsStrict else NotStrict,+ pairToType mps backend (fieldType, nullable fieldAttrs) ) (nameFinal, paramsFinal) | mpsGeneric mps = (nameG, [PlainTV backend])@@ -183,14 +219,14 @@ | entitySum t = map sumCon $ entityFields t | otherwise = [RecC name cols] - sumCon fd@(FieldDef _ _ ty _) = NormalC+ sumCon fd = NormalC (sumConstrName t fd)- [(NotStrict, pairToType mps backend (ty, NotNullable))]+ [(NotStrict, pairToType mps backend (fieldType fd, NotNullable))] -sumConstrName :: EntityDef -> FieldDef -> Name-sumConstrName t (FieldDef n _ _ _) = mkName $ unpack $ concat+sumConstrName :: EntityDef a -> FieldDef b -> Name+sumConstrName t FieldDef {..} = mkName $ unpack $ concat [ unHaskellName $ entityHaskell t- , upperFirst $ unHaskellName n+ , upperFirst $ unHaskellName fieldHaskell , "Sum" ] @@ -200,13 +236,13 @@ (x, _):_ -> Just x [] -> Nothing -entityUpdates :: EntityDef -> [(HaskellName, FieldType, IsNullable, PersistUpdate)]+entityUpdates :: EntityDef a -> [(HaskellName, FieldType, IsNullable, PersistUpdate)] entityUpdates = concatMap go . entityFields where- go (FieldDef x _ y as) = map (\a -> (x, y, nullable as, a)) [minBound..maxBound]+ go FieldDef {..} = map (\a -> (fieldHaskell, fieldType, nullable fieldAttrs, a)) [minBound..maxBound] -uniqueTypeDec :: MkPersistSettings -> EntityDef -> Dec+uniqueTypeDec :: MkPersistSettings -> EntityDef a -> Dec uniqueTypeDec mps t = DataInstD [] ''Unique [genericDataType mps (unHaskellName $ entityHaskell t) $ VarT backend]@@ -215,7 +251,7 @@ where backend = mkName "backend" -mkUnique :: MkPersistSettings -> Name -> EntityDef -> UniqueDef -> Con+mkUnique :: MkPersistSettings -> Name -> EntityDef a -> UniqueDef -> Con mkUnique mps backend t (UniqueDef (HaskellName constr) _ fields attrs) = NormalC (mkName $ unpack constr) types where@@ -228,11 +264,11 @@ go (_, Nullable _) | not force = error nullErrMsg go (ft, y) = (NotStrict, pairToType mps backend (ft, y)) - lookup3 :: Text -> [FieldDef] -> (FieldType, IsNullable)+ lookup3 :: Text -> [FieldDef a] -> (FieldType, IsNullable) lookup3 s [] = error $ unpack $ "Column not found: " ++ s ++ " in unique " ++ constr- lookup3 x ((FieldDef (HaskellName x') _ y z):rest)- | x == x' = (y, nullable z)+ lookup3 x (FieldDef {..}:rest)+ | x == unHaskellName fieldHaskell = (fieldType, nullable fieldAttrs) | otherwise = lookup3 x rest nullErrMsg =@@ -250,7 +286,7 @@ -> (FieldType, IsNullable) -> Type pairToType mps backend (s, Nullable ByMaybeAttr) =- ConT (mkName "Maybe") `AppT` idType mps backend s+ ConT ''Maybe `AppT` idType mps backend s pairToType mps backend (s, _) = idType mps backend s backendDataType :: MkPersistSettings -> Type@@ -281,18 +317,18 @@ degen :: [Clause] -> [Clause] degen [] =- let err = VarE (mkName "error") `AppE` LitE (StringL+ let err = VarE 'error `AppE` LitE (StringL "Degenerate case, should never happen") in [Clause [WildP] (NormalB err) []] degen x = x -mkToPersistFields :: String -> EntityDef -> Q Dec+mkToPersistFields :: String -> EntityDef a -> Q Dec mkToPersistFields constr ed@EntityDef { entitySum = isSum, entityFields = fields } = do clauses <- if isSum then sequence $ zipWith goSum fields [1..] else fmap return go- return $ FunD (mkName "toPersistFields") clauses+ return $ FunD 'toPersistFields clauses where go :: Q Clause go = do@@ -304,7 +340,7 @@ fieldCount = length fields - goSum :: FieldDef -> Int -> Q Clause+ goSum :: FieldDef a -> Int -> Q Clause goSum fd idx = do let name = sumConstrName ed fd enull <- [|SomePersistField PersistNull|]@@ -325,7 +361,7 @@ mkToFieldNames :: [UniqueDef] -> Q Dec mkToFieldNames pairs = do pairs' <- mapM go pairs- return $ FunD (mkName "persistUniqueToFieldNames") $ degen pairs'+ return $ FunD 'persistUniqueToFieldNames $ degen pairs' where go (UniqueDef constr _ names _) = do names' <- lift names@@ -347,7 +383,7 @@ mkUniqueToValues :: [UniqueDef] -> Q Dec mkUniqueToValues pairs = do pairs' <- mapM go pairs- return $ FunD (mkName "persistUniqueToValues") $ degen pairs'+ return $ FunD 'persistUniqueToValues $ degen pairs' where go :: UniqueDef -> Q Clause go (UniqueDef constr _ names _) = do@@ -364,37 +400,23 @@ go (constr, name) = Clause [RecP (mkName constr) []] (NormalB $ LitE $ StringL name) [] -mkToOrder :: [(String, Exp)] -> Dec-mkToOrder pairs =- FunD (mkName "persistOrderToOrder") $ degen $ map go pairs- where- go (constr, val) =- Clause [RecP (mkName constr) []] (NormalB val) []- mkToValue :: String -> [String] -> Dec mkToValue func = FunD (mkName func) . degen . map go where go constr = let x = mkName "x" in Clause [ConP (mkName constr) [VarP x]]- (NormalB $ VarE (mkName "toPersistValue") `AppE` VarE x)+ (NormalB $ VarE 'toPersistValue `AppE` VarE x) [] -mkHalfDefined :: Name -> Int -> Dec-mkHalfDefined constr count' =- FunD (mkName "halfDefined")- [Clause [] (NormalB- $ foldl AppE (ConE constr)- (replicate count' $ VarE $ mkName "undefined")) []]- isNotNull :: PersistValue -> Bool isNotNull PersistNull = False isNotNull _ = True -mkFromPersistValues :: EntityDef -> Q [Clause]+mkFromPersistValues :: EntityDef a -> Q [Clause] mkFromPersistValues t@(EntityDef { entitySum = False }) = do- nothing <- [|Left $(liftT "Invalid fromPersistValues input")|]- let cons' = ConE $ mkName $ unpack $ unHaskellName $ entityHaskell t+ nothing <- [|Left $(liftT $ "Invalid fromPersistValues input. Entity: " `mappend` entName)|]+ let cons' = ConE $ mkName $ unpack $ entName xs <- mapM (const $ newName "x") $ entityFields t fs <- [|fromPersistValue|] let xs' = map (AppE fs . VarE) xs@@ -407,12 +429,15 @@ , Clause [WildP] (NormalB nothing) [] ] where+ entName = unHaskellName $ entityHaskell t go ap' x y = InfixE (Just x) ap' (Just y)+ mkFromPersistValues t@(EntityDef { entitySum = True }) = do- nothing <- [|Left $(liftT "Invalid fromPersistValues input: sum type with all nulls")|]+ nothing <- [|Left $(liftT $ "Invalid fromPersistValues input: sum type with all nulls. Entity: " `mappend` entName)|] clauses <- mkClauses [] $ entityFields t return $ clauses `mappend` [Clause [WildP] (NormalB nothing) []] where+ entName = unHaskellName $ entityHaskell t mkClauses _ [] = return [] mkClauses before (field:after) = do x <- newName "x"@@ -435,7 +460,7 @@ lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b lens sa sbt afb s = fmap (sbt s) (afb $ sa s) -mkLensClauses :: EntityDef -> Q [Clause]+mkLensClauses :: EntityDef a -> Q [Clause] mkLensClauses t = do lens' <- [|lens|] getId <- [|entityKey|]@@ -488,7 +513,7 @@ ] $ ConE 'Entity `AppE` VarE keyName `AppE` (ConE (sumConstrName t f) `AppE` VarE xName) -mkEntity :: MkPersistSettings -> EntityDef -> Q [Dec]+mkEntity :: MkPersistSettings -> EntityDef SqlType -> Q [Dec] mkEntity mps t = do t' <- lift t let nameT = unHaskellName $ entityHaskell t@@ -500,10 +525,14 @@ puk <- mkUniqueKeys t fields <- mapM (mkField mps t) $ FieldDef- (HaskellName "Id")- (entityID t)- (FTTypeCon Nothing $ unHaskellName (entityHaskell t) ++ "Id")- []+ { fieldHaskell = HaskellName "Id"+ , fieldDB = entityID t+ , fieldType = FTTypeCon Nothing $ unHaskellName (entityHaskell t) ++ "Id"+ , fieldSqlType = SqlInt64+ , fieldEmbedded = Nothing+ , fieldAttrs = []+ , fieldStrict = True+ } : entityFields t toFieldNames <- mkToFieldNames $ entityUniques t @@ -521,14 +550,9 @@ ConT ''KeyBackend `AppT` mpsBackend mps `AppT` ConT (mkName nameS) , InstanceD [] clazz $ [ uniqueTypeDec mps t- , FunD (mkName "entityDef") [Clause [WildP] (NormalB t') []]+ , FunD 'entityDef [Clause [WildP] (NormalB t') []] , tpf- , FunD (mkName "fromPersistValues") fpv- , mkHalfDefined- (if entitySum t- then sumConstrName t (head $ entityFields t)- else mkName nameS)- (if entitySum t then 1 else length $ entityFields t)+ , FunD 'fromPersistValues fpv , toFieldNames , utv , puk@@ -540,13 +564,13 @@ ] (map fst fields) []- , FunD (mkName "persistFieldDef") (map snd fields)+ , FunD 'persistFieldDef (map snd fields) , TySynInstD- (mkName "PersistEntityBackend")+ ''PersistEntityBackend [genericDataType mps (unHaskellName $ entityHaskell t) $ VarT $ mkName "backend"] (backendDataType mps)- , FunD (mkName "persistIdField") [Clause [] (NormalB $ ConE $ mkName $ unpack $ unHaskellName (entityHaskell t) ++ "Id") []]- , FunD (mkName "fieldLens") lensClauses+ , FunD 'persistIdField [Clause [] (NormalB $ ConE $ mkName $ unpack $ unHaskellName (entityHaskell t) ++ "Id") []]+ , FunD 'fieldLens lensClauses ] ] @@ -560,7 +584,7 @@ -- Right r -> r) o -- fromPersistValue x = Left $ "Expected PersistMap, received: " ++ show x -- sqlType _ = SqlString-persistFieldFromEntity :: MkPersistSettings -> EntityDef -> Q [Dec]+persistFieldFromEntity :: MkPersistSettings -> EntityDef a -> Q [Dec] persistFieldFromEntity mps e = do ss <- [|SqlString|] let columnNames = map (unpack . unHaskellName . fieldHaskell) (entityFields e)@@ -574,12 +598,14 @@ getPersistMap' <- [|getPersistMap|] return [ persistFieldInstanceD typ- [ sqlTypeFunD ss- , FunD (mkName "toPersistValue") [ Clause [] (NormalB obj) [] ]- , FunD (mkName "fromPersistValue")+ [ FunD 'toPersistValue [ Clause [] (NormalB obj) [] ]+ , FunD 'fromPersistValue [ Clause [] (NormalB $ InfixE (Just fpv) compose $ Just getPersistMap') [] ] ]+ , persistFieldSqlInstanceD typ+ [ sqlTypeFunD ss+ ] ] where entityName = (unpack $ unHaskellName $ entityHaskell e)@@ -598,22 +624,11 @@ -- This function is useful for cases such as: -- -- >>> share [mkSave "myDefs", mkPersist sqlSettings] [persistLowerCase|...|]-share :: [[EntityDef] -> Q [Dec]] -> [EntityDef] -> Q [Dec]+share :: [[EntityDef a] -> Q [Dec]] -> [EntityDef a] -> Q [Dec] share fs x = fmap mconcat $ mapM ($ x) fs --- | Deprecated, restricted version of 'share'.-share2 :: ([EntityDef] -> Q [Dec])- -> ([EntityDef] -> Q [Dec])- -> [EntityDef]- -> Q [Dec]-share2 f g x = do- y <- f x- z <- g x- return $ y `mappend` z-{-# DEPRECATED share2 "Use share instead" #-}- -- | Save the @EntityDef@s passed in under the given name.-mkSave :: String -> [EntityDef] -> Q [Dec]+mkSave :: String -> [EntityDef SqlType] -> Q [Dec] mkSave name' defs' = do let name = mkName name' defs <- lift defs'@@ -629,27 +644,27 @@ } -- | Generate a 'DeleteCascade' instance for the given @EntityDef@s.-mkDeleteCascade :: MkPersistSettings -> [EntityDef] -> Q [Dec]+mkDeleteCascade :: MkPersistSettings -> [EntityDef a] -> Q [Dec] mkDeleteCascade mps defs = do let deps = concatMap getDeps defs mapM (go deps) defs where- getDeps :: EntityDef -> [Dep]+ getDeps :: EntityDef a -> [Dep] getDeps def = concatMap getDeps' $ entityFields def where- getDeps' :: FieldDef -> [Dep]- getDeps' (FieldDef name _ ftyp attribs) =- case stripId ftyp of+ getDeps' :: FieldDef a -> [Dep]+ getDeps' FieldDef {..} =+ case stripId fieldType of Just f -> return Dep { depTarget = f , depSourceTable = entityHaskell def- , depSourceField = name- , depSourceNull = nullable attribs+ , depSourceField = fieldHaskell+ , depSourceNull = nullable fieldAttrs } Nothing -> []- go :: [Dep] -> EntityDef -> Q Dec+ go :: [Dep] -> EntityDef a -> Q Dec go allDeps EntityDef{entityHaskell = name} = do let deps = filter (\x -> depTarget x == unHaskellName name) allDeps key <- newName "key"@@ -687,19 +702,20 @@ , EqualP (ConT ''PersistEntityBackend `AppT` entityT) (ConT ''PersistMonadBackend `AppT` VarT (mkName "m")) ] (ConT ''DeleteCascade `AppT` entityT `AppT` VarT (mkName "m"))- [ FunD (mkName "deleteCascade")+ [ FunD 'deleteCascade [Clause [VarP key] (NormalB $ DoE stmts) []] ] -mkUniqueKeys :: EntityDef -> Q Dec+mkUniqueKeys :: EntityDef a -> Q Dec mkUniqueKeys def | entitySum def =- return $ FunD (mkName "persistUniqueKeys") [Clause [WildP] (NormalB $ ListE []) []]+ return $ FunD 'persistUniqueKeys [Clause [WildP] (NormalB $ ListE []) []] mkUniqueKeys def = do c <- clause- return $ FunD (mkName "persistUniqueKeys") [c]+ return $ FunD 'persistUniqueKeys [c] where clause = do- xs <- forM (entityFields def) $ \(FieldDef x _ _ _) -> do+ xs <- forM (entityFields def) $ \fd -> do+ let x = fieldHaskell fd x' <- newName $ '_' : unpack (unHaskellName x) return (x, x') let pcs = map (go xs) $ entityUniques def@@ -718,13 +734,17 @@ in front `AppE` VarE col' sqlTypeFunD :: Exp -> Dec-sqlTypeFunD st = FunD (mkName "sqlType")+sqlTypeFunD st = FunD 'sqlType [ Clause [WildP] (NormalB st) [] ] persistFieldInstanceD :: Type -> [Dec] -> Dec persistFieldInstanceD typ = InstanceD [] (ConT ''PersistField `AppT` typ) +persistFieldSqlInstanceD :: Type -> [Dec] -> Dec+persistFieldSqlInstanceD typ =+ InstanceD [] (ConT ''PersistFieldSql `AppT` typ)+ -- | Automatically creates a valid 'PersistField' instance for any datatype -- that has valid 'Show' and 'Read' instances. Can be very convenient for -- 'Enum' types.@@ -738,24 +758,26 @@ Right s' -> case reads $ unpack s' of (x, _):_ -> Right x- [] -> Left $ "Invalid " ++ dt ++ ": " ++ s'|]+ [] -> Left $ pack "Invalid " ++ pack dt ++ pack ": " ++ s'|] return [ persistFieldInstanceD (ConT $ mkName s)- [ sqlTypeFunD ss- , FunD (mkName "toPersistValue")+ [ FunD 'toPersistValue [ Clause [] (NormalB tpv) [] ]- , FunD (mkName "fromPersistValue")+ , FunD 'fromPersistValue [ Clause [] (NormalB $ fpv `AppE` LitE (StringL s)) [] ] ]+ , persistFieldSqlInstanceD (ConT $ mkName s)+ [ sqlTypeFunD ss+ ] ] -- | Creates a single function to perform all migrations for the entities -- defined here. One thing to be aware of is dependencies: if you have entities -- with foreign references, make sure to place those definitions after the -- entities they reference.-mkMigrate :: String -> [EntityDef] -> Q [Dec]+mkMigrate :: Lift' a => String -> [EntityDef a] -> Q [Dec] mkMigrate fun allDefs = do body' <- body return@@ -768,8 +790,9 @@ typ = ForallT [PlainTV $ mkName "m"] [ ClassP ''MonadBaseControl [ConT ''IO, VarT $ mkName "m"] , ClassP ''MonadIO [VarT $ mkName "m"]+ , ClassP ''MonadLogger [VarT $ mkName "m"] ]- $ ConT ''Migration `AppT` (ConT ''SqlPersist `AppT` VarT (mkName "m"))+ $ ConT ''Migration `AppT` (ConT ''SqlPersistT `AppT` VarT (mkName "m")) body :: Q Exp body = case defs of@@ -777,21 +800,18 @@ _ -> do defsName <- newName "defs" defsStmt <- do- u <- [|undefined|]- e <- [|entityDef|]- let defsExp = ListE $ map (AppE e . undefinedEntityTH u) defs+ defs' <- mapM lift defs+ let defsExp = ListE defs' return $ LetS [ValD (VarP defsName) (NormalB defsExp) []] stmts <- mapM (toStmt $ VarE defsName) defs return (DoE $ defsStmt : stmts)- toStmt :: Exp -> EntityDef -> Q Stmt+ toStmt :: Lift' a => Exp -> EntityDef a -> Q Stmt toStmt defsExp ed = do- u <- [|undefined|]+ u <- lift ed m <- [|migrate|]- return $ NoBindS $ m `AppE` defsExp `AppE` (undefinedEntityTH u ed)- undefinedEntityTH :: Exp -> EntityDef -> Exp- undefinedEntityTH u = SigE u . ConT . mkName . unpack . unHaskellName . entityHaskell+ return $ NoBindS $ m `AppE` defsExp `AppE` u -instance Lift EntityDef where+instance Lift' a => Lift (EntityDef a) where lift (EntityDef a b c d e f g h i) = [|EntityDef $(lift a)@@ -804,11 +824,26 @@ $(liftMap h) $(lift i) |]-instance Lift FieldDef where- lift (FieldDef a b c d) = [|FieldDef $(lift a) $(lift b) $(lift c) $(liftTs d)|]+instance Lift' a => Lift (FieldDef a) where+ lift (FieldDef a b c d e f g) = [|FieldDef a b c $(lift' d) $(liftTs e) f $(lift' g)|] instance Lift UniqueDef where lift (UniqueDef a b c d) = [|UniqueDef $(lift a) $(lift b) $(lift c) $(liftTs d)|] +-- | A hack to avoid orphans.+class Lift' a where+ lift' :: a -> Q Exp+instance Lift' SqlType where+ lift' = lift+instance Lift' a => Lift' (Maybe a) where+ lift' Nothing = [|Nothing|]+ lift' (Just a) = [|Just $(lift' a)|]+instance Lift' a => Lift' (EntityDef a) where+ lift' = lift+instance Lift' () where+ lift' () = [|()|]+instance Lift' SqlTypeExp where+ lift' = lift+ pack' :: String -> Text pack' = pack #if !MIN_VERSION_text(0, 11, 2)@@ -858,13 +893,31 @@ lift Multiply = [|Multiply|] lift Divide = [|Divide|] +instance Lift SqlType where+ lift SqlString = [|SqlString|]+ lift SqlInt32 = [|SqlInt32|]+ lift SqlInt64 = [|SqlInt64|]+ lift SqlReal = [|SqlReal|]+ lift (SqlNumeric x y) =+ [|SqlNumeric (fromInteger x') (fromInteger y')|]+ where+ x' = fromIntegral x+ y' = fromIntegral y+ lift SqlBool = [|SqlBool|]+ lift SqlDay = [|SqlDay|]+ lift SqlTime = [|SqlTime|]+ lift SqlDayTime = [|SqlDayTime|]+ lift SqlDayTimeZoned = [|SqlDayTimeZoned|]+ lift SqlBlob = [|SqlBlob|]+ lift (SqlOther a) = [|SqlOther $(liftT a)|]+ -- Ent -- fieldName FieldType -- -- forall . typ ~ FieldType => EntFieldName -- -- EntFieldName = FieldDef ....-mkField :: MkPersistSettings -> EntityDef -> FieldDef -> Q (Con, Clause)+mkField :: MkPersistSettings -> EntityDef a -> FieldDef SqlType -> Q (Con, Clause) mkField mps et cd = do let con = ForallC []@@ -905,7 +958,7 @@ (++) :: Text -> Text -> Text (++) = append -mkJSON :: MkPersistSettings -> EntityDef -> Q [Dec]+mkJSON :: MkPersistSettings -> EntityDef a -> Q [Dec] mkJSON _ def | not ("json" `elem` entityAttrs def) = return [] mkJSON mps def = do pureE <- [|pure|]
persistent-template.cabal view
@@ -1,5 +1,5 @@ name: persistent-template-version: 1.1.3.1+version: 1.2.0 license: MIT license-file: LICENSE author: Michael Snoyman <michael@snoyman.com>@@ -16,12 +16,13 @@ library build-depends: base >= 4 && < 5 , template-haskell- , persistent >= 1.1.2 && < 1.2+ , persistent >= 1.2 && < 1.3 , monad-control >= 0.2 && < 0.4 , text >= 0.5 && < 1.0 , transformers >= 0.2 && < 0.4 , containers , aeson+ , monad-logger exposed-modules: Database.Persist.TH ghc-options: -Wall if impl(ghc >= 7.4)
test/main.hs view
@@ -8,7 +8,6 @@ import Control.Applicative ((<$>), (<*>)) import Database.Persist-import Database.Persist.Store (DeleteCascade (..)) import Database.Persist.TH import Data.Text (Text, pack) import Data.Aeson