packages feed

groundhog-th 0.5.1 → 0.6.0

raw patch · 5 files changed

+248/−202 lines, 5 filesdep +aesondep +unordered-containersdep ~groundhogPVP ok

version bump matches the API change (PVP)

Dependencies added: aeson, unordered-containers

Dependency ranges changed: groundhog

API changes (from Hackage documentation)

- Database.Groundhog.TH.Settings: instance FromJSON PSFieldDef
- Database.Groundhog.TH.Settings: instance Lift PSFieldDef
+ Database.Groundhog.TH: defaultMkEmbeddedDecs :: THEmbeddedDef -> Q [Dec]
+ Database.Groundhog.TH: defaultMkEntityDecs :: THEntityDef -> Q [Dec]
+ Database.Groundhog.TH: defaultMkPrimitiveDecs :: THPrimitiveDef -> Q [Dec]
+ Database.Groundhog.TH: mkEmbeddedDecs :: CodegenConfig -> [THEmbeddedDef -> Q [Dec]]
+ Database.Groundhog.TH: mkEntityDecs :: CodegenConfig -> [THEntityDef -> Q [Dec]]
+ Database.Groundhog.TH: mkPrimitiveDecs :: CodegenConfig -> [THPrimitiveDef -> Q [Dec]]
+ Database.Groundhog.TH: mkTHEntityDef :: NamingStyle -> Dec -> THEntityDef
+ Database.Groundhog.TH.Settings: instance FromJSON (PSFieldDef String)
+ Database.Groundhog.TH.Settings: instance Lift (PSFieldDef String)
- Database.Groundhog.TH: CodegenConfig :: NamingStyle -> Maybe String -> CodegenConfig
+ Database.Groundhog.TH: CodegenConfig :: NamingStyle -> Maybe String -> [THEntityDef -> Q [Dec]] -> [THEmbeddedDef -> Q [Dec]] -> [THPrimitiveDef -> Q [Dec]] -> CodegenConfig
- Database.Groundhog.TH.Settings: PSConstructorDef :: String -> Maybe String -> Maybe String -> Maybe (Maybe String) -> Maybe [PSFieldDef] -> Maybe [PSUniqueDef] -> PSConstructorDef
+ Database.Groundhog.TH.Settings: PSConstructorDef :: String -> Maybe String -> Maybe String -> Maybe (Maybe String) -> Maybe [PSFieldDef String] -> Maybe [PSUniqueDef] -> PSConstructorDef
- Database.Groundhog.TH.Settings: PSEmbeddedDef :: String -> Maybe String -> Maybe [PSFieldDef] -> PSEmbeddedDef
+ Database.Groundhog.TH.Settings: PSEmbeddedDef :: String -> Maybe String -> Maybe [PSFieldDef String] -> PSEmbeddedDef
- Database.Groundhog.TH.Settings: PSFieldDef :: String -> Maybe String -> Maybe String -> Maybe String -> Maybe [PSFieldDef] -> Maybe String -> Maybe (Maybe (Maybe String, String, [String]), Maybe ReferenceActionType, Maybe ReferenceActionType) -> PSFieldDef
+ Database.Groundhog.TH.Settings: PSFieldDef :: str -> Maybe str -> Maybe str -> Maybe str -> Maybe [PSFieldDef str] -> Maybe str -> Maybe (Maybe (Maybe str, str, [str]), Maybe ReferenceActionType, Maybe ReferenceActionType) -> PSFieldDef str
- Database.Groundhog.TH.Settings: PSUniqueDef :: String -> Maybe UniqueType -> [String] -> PSUniqueDef
+ Database.Groundhog.TH.Settings: PSUniqueDef :: String -> Maybe UniqueType -> [Either String String] -> PSUniqueDef
- Database.Groundhog.TH.Settings: PSUniqueKeyDef :: String -> Maybe String -> Maybe String -> Maybe String -> Maybe [PSFieldDef] -> Maybe Bool -> Maybe Bool -> PSUniqueKeyDef
+ Database.Groundhog.TH.Settings: PSUniqueKeyDef :: String -> Maybe String -> Maybe String -> Maybe String -> Maybe [PSFieldDef String] -> Maybe Bool -> Maybe Bool -> PSUniqueKeyDef
- Database.Groundhog.TH.Settings: THFieldDef :: String -> String -> Maybe String -> String -> Type -> Maybe [PSFieldDef] -> Maybe String -> Maybe (Maybe (Maybe String, String, [String]), Maybe ReferenceActionType, Maybe ReferenceActionType) -> THFieldDef
+ Database.Groundhog.TH.Settings: THFieldDef :: String -> String -> Maybe String -> String -> Type -> Maybe [PSFieldDef String] -> Maybe String -> Maybe (Maybe (Maybe String, String, [String]), Maybe ReferenceActionType, Maybe ReferenceActionType) -> THFieldDef
- Database.Groundhog.TH.Settings: THUniqueDef :: String -> UniqueType -> [String] -> THUniqueDef
+ Database.Groundhog.TH.Settings: THUniqueDef :: String -> UniqueType -> [Either String String] -> THUniqueDef
- Database.Groundhog.TH.Settings: data PSFieldDef :: *
+ Database.Groundhog.TH.Settings: data PSFieldDef str :: * -> *
- Database.Groundhog.TH.Settings: psConstrFields :: PSConstructorDef -> Maybe [PSFieldDef]
+ Database.Groundhog.TH.Settings: psConstrFields :: PSConstructorDef -> Maybe [PSFieldDef String]
- Database.Groundhog.TH.Settings: psDbFieldName :: PSFieldDef -> Maybe String
+ Database.Groundhog.TH.Settings: psDbFieldName :: PSFieldDef str -> Maybe str
- Database.Groundhog.TH.Settings: psDbTypeName :: PSFieldDef -> Maybe String
+ Database.Groundhog.TH.Settings: psDbTypeName :: PSFieldDef str -> Maybe str
- Database.Groundhog.TH.Settings: psDefaultValue :: PSFieldDef -> Maybe String
+ Database.Groundhog.TH.Settings: psDefaultValue :: PSFieldDef str -> Maybe str
- Database.Groundhog.TH.Settings: psEmbeddedDef :: PSFieldDef -> Maybe [PSFieldDef]
+ Database.Groundhog.TH.Settings: psEmbeddedDef :: PSFieldDef str -> Maybe [PSFieldDef str]
- Database.Groundhog.TH.Settings: psEmbeddedFields :: PSEmbeddedDef -> Maybe [PSFieldDef]
+ Database.Groundhog.TH.Settings: psEmbeddedFields :: PSEmbeddedDef -> Maybe [PSFieldDef String]
- Database.Groundhog.TH.Settings: psExprName :: PSFieldDef -> Maybe String
+ Database.Groundhog.TH.Settings: psExprName :: PSFieldDef str -> Maybe str
- Database.Groundhog.TH.Settings: psFieldName :: PSFieldDef -> String
+ Database.Groundhog.TH.Settings: psFieldName :: PSFieldDef str -> str
- Database.Groundhog.TH.Settings: psReferenceParent :: PSFieldDef -> Maybe (Maybe (Maybe String, String, [String]), Maybe ReferenceActionType, Maybe ReferenceActionType)
+ Database.Groundhog.TH.Settings: psReferenceParent :: PSFieldDef str -> Maybe (Maybe (Maybe str, str, [str]), Maybe ReferenceActionType, Maybe ReferenceActionType)
- Database.Groundhog.TH.Settings: psUniqueFields :: PSUniqueDef -> [String]
+ Database.Groundhog.TH.Settings: psUniqueFields :: PSUniqueDef -> [Either String String]
- Database.Groundhog.TH.Settings: psUniqueKeyFields :: PSUniqueKeyDef -> Maybe [PSFieldDef]
+ Database.Groundhog.TH.Settings: psUniqueKeyFields :: PSUniqueKeyDef -> Maybe [PSFieldDef String]
- Database.Groundhog.TH.Settings: thEmbeddedDef :: THFieldDef -> Maybe [PSFieldDef]
+ Database.Groundhog.TH.Settings: thEmbeddedDef :: THFieldDef -> Maybe [PSFieldDef String]
- Database.Groundhog.TH.Settings: thUniqueFields :: THUniqueDef -> [String]
+ Database.Groundhog.TH.Settings: thUniqueFields :: THUniqueDef -> [Either String String]

Files

Database/Groundhog/TH.hs view
@@ -11,6 +11,9 @@   -- * Settings for code generation   , CodegenConfig(..)   , defaultCodegenConfig+  , defaultMkEntityDecs+  , defaultMkEmbeddedDecs+  , defaultMkPrimitiveDecs   -- $namingStylesDoc   , NamingStyle(..)   , suffixNamingStyle@@ -19,6 +22,8 @@   , lowerCaseSuffixNamingStyle   , toUnderscore   , firstChar+  -- * Utility functions+  , mkTHEntityDef   ) where  import Database.Groundhog.Core (delim, UniqueType(..))@@ -28,25 +33,29 @@ import Language.Haskell.TH import Language.Haskell.TH.Syntax (StrictType, VarStrictType, Lift(..)) import Language.Haskell.TH.Quote-import Control.Applicative ((<$>))+import Control.Applicative import Control.Monad (forM, forM_, when, unless, liftM2) import Data.Char (isUpper, isLower, isSpace, isDigit, toUpper, toLower) import Data.List (nub, (\\)) import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing) import Data.String import Data.Text.Encoding (encodeUtf8)-import Data.Yaml as Y(decodeHelper, ParseException(..))+import Data.Yaml as Y (decodeHelper, ParseException(..)) import qualified Text.Libyaml as Y  data CodegenConfig = CodegenConfig {-    -- | Naming style that is applied for all definitions+  -- | Naming style that is applied for all definitions     namingStyle :: NamingStyle-    -- | Codegenerator will create a function with this name that will run 'migrate' for each non-polymorphic entity in definition+  -- | Codegenerator will create a function with this name that will run 'migrate' for each non-polymorphic entity in definition   , migrationFunction :: Maybe String+  -- | Functions that produce Haskell code for the entities. In most cases when overriding, the default functions that produce mappings are not replaced but kept along with custom code. Example: @['defaultMkEntityDecs', mkMyDecs]@.+  , mkEntityDecs :: [THEntityDef -> Q [Dec]]+  , mkEmbeddedDecs :: [THEmbeddedDef -> Q [Dec]]+  , mkPrimitiveDecs :: [THPrimitiveDef -> Q [Dec]] }  defaultCodegenConfig :: CodegenConfig-defaultCodegenConfig = CodegenConfig suffixNamingStyle Nothing+defaultCodegenConfig = CodegenConfig suffixNamingStyle Nothing [defaultMkEntityDecs] [defaultMkEmbeddedDecs] [defaultMkPrimitiveDecs]  -- $namingStylesDoc -- When describing a datatype you can omit the most of the declarations. @@ -203,7 +212,7 @@   primitives <- catMaybes <$> forM defs (\d -> case d of     PSPrimitiveDef'     e -> Just .                                 applyPrimitiveSettings e               . mkTHPrimitiveDef namingStyle     <$> getDecl (psPrimitiveName e)     _ -> return Nothing)-  decs <- fmap (concat . concat) $ sequence [mapM mkEntityDecs entities, mapM mkEmbeddedDecs embeddeds, mapM mkPrimitiveDecs primitives]+  decs <- fmap concat $ sequence $ (mkEntityDecs <*> entities) ++ ( mkEmbeddedDecs <*> embeddeds) ++ (mkPrimitiveDecs <*> primitives)   migrateFunc <- maybe (return []) (\name -> mkMigrateFunction name entities) migrationFunction   return $ migrateFunc ++ decs @@ -255,7 +264,7 @@   f = foldr $ replaceOne "field" psFieldName thFieldName applyFieldSettings   convertUnique (PSUniqueDef uName uType uFields) = THUniqueDef uName (fromMaybe UniqueConstraint uType) uFields   -applyFieldSettings :: PSFieldDef -> THFieldDef -> THFieldDef+applyFieldSettings :: PSFieldDef String -> THFieldDef -> THFieldDef applyFieldSettings PSFieldDef{..} def@(THFieldDef{..}) =   def { thDbFieldName = fromMaybe thDbFieldName psDbFieldName       , thExprName = fromMaybe thExprName psExprName@@ -280,8 +289,9 @@  mkFieldsForUniqueKey :: NamingStyle -> String -> THUniqueKeyDef -> THConstructorDef -> [THFieldDef] mkFieldsForUniqueKey style dName uniqueKey cDef = zipWith (setSelector . findField) (thUniqueFields uniqueDef) [0..] where-  findField name = findOne "field" id thFieldName name $ thConstrFields cDef-  uniqueDef = findOne "unique" id thUniqueName (thUniqueKeyName uniqueKey) $ thConstrUniques cDef+  findField (Left name) = findOne "field" thFieldName name $ thConstrFields cDef+  findField (Right expr) = error $ "A unique key may not contain expressions: " ++ expr+  uniqueDef = findOne "unique" thUniqueName (thUniqueKeyName uniqueKey) $ thConstrUniques cDef   setSelector f i = f {thExprName = mkExprSelectorName style dName (thUniqueKeyConstrName uniqueKey) (thFieldName f) i}  notUniqueBy :: Eq b => (a -> b) -> [a] -> [b]@@ -328,14 +338,11 @@     then when (primaryConstraints > 0) $            fail $ "Custom primary keys may exist only for datatypes with single constructor: " ++ show (thDataName def)     else when (primaryConstraints + maybe 0 (const 1) (thAutoKey def) > 1) $-           fail $ "A datatype may have either an auto key or one custom primary key constraint : " ++ show (thDataName def)-  -- check that if unique keys = [] there is auto key-  when (null (thUniqueKeys def) && isNothing (thAutoKey def)) $-    fail $ "A datatype must have either an auto key or unique keys: " ++ show (thDataName def)+           fail $ "A datatype cannot have more than one primary key constraint: " ++ show (thDataName def)   -- check that only one of the keys is default-  let defaults = maybe False thAutoKeyIsDef (thAutoKey def) : map thUniqueKeyIsDef (thUniqueKeys def)-  when (length (filter id defaults) /= 1) $-    fail $ "A datatype must have exactly one default key: " ++ show (thDataName def)+  let keyDefaults = maybe id ((:) . thAutoKeyIsDef) (thAutoKey def) $ map thUniqueKeyIsDef (thUniqueKeys def)+  when (not (null keyDefaults) && length (filter id keyDefaults) /= 1) $+    fail $ "A datatype with keys must have one default key: " ++ show (thDataName def)   return def    validateField :: THFieldDef -> Either String ()@@ -432,7 +439,7 @@ --    \-- The declaration with defaulted names -- --mkPersist defaultCodegenConfig [groundhog|---entity: Settable                       # If we did not want to add a constraint, this line would be enough+--entity: Settable                       # If we did not have a constraint, this line would be enough --keys: --  - name: someconstraint --constructors:@@ -493,7 +500,12 @@ --        uniques: --          - name: someconstraint --            type: constraint           # The type can be be \"constraint\", \"index\", or \"primary\"---            fields: [foo, bar]         # List of constructor parameter names. Not DB names(!)+--            fields: [foo, bar]         # List of constructor parameter names. Not column names.+--   # This is example for databases which support expression indexes.+--   # Note that for checking index during migration expression should be written in exactly the same form as database returns.+--   #  - name: myuniqueindex+--   #    type: index+--   #    fields: [foo, {expr: "(bar + 1)" }] -- |] -- @ --@@ -579,45 +591,45 @@     Right (Left err) -> fail err     Right (Right result') -> lift (result' :: PersistDefinitions) -mkEntityDecs :: THEntityDef -> Q [Dec]-mkEntityDecs def = do+defaultMkEntityDecs :: THEntityDef -> Q [Dec]+defaultMkEntityDecs def = do   --runIO (print def)-  decs <- fmap concat $ sequence-    [ mkEntityPhantomConstructors def-    , mkEntityPhantomConstructorInstances def-    , mkAutoKeyPersistFieldInstance def-    , mkAutoKeyPrimitivePersistFieldInstance def-    , mkEntityUniqueKeysPhantoms def-    , mkUniqueKeysIsUniqueInstances def-    , mkUniqueKeysEmbeddedInstances def-    , mkUniqueKeysPersistFieldInstances def-    , mkUniqueKeysPrimitiveOrPurePersistFieldInstances def-    , mkKeyEqShowInstances def-    , mkEntityPersistFieldInstance def-    , mkEntitySinglePersistFieldInstance def-    , mkPersistEntityInstance def-    , mkEntityNeverNullInstance def+  decs <- fmap concat $ sequence $ map ($ def)+    [ mkEntityPhantomConstructors+    , mkEntityPhantomConstructorInstances+    , mkAutoKeyPersistFieldInstance+    , mkAutoKeyPrimitivePersistFieldInstance+    , mkEntityUniqueKeysPhantoms+    , mkUniqueKeysIsUniqueInstances+    , mkUniqueKeysEmbeddedInstances+    , mkUniqueKeysPersistFieldInstances+    , mkUniqueKeysPrimitiveOrPurePersistFieldInstances+    , mkKeyEqShowInstances+    , mkEntityPersistFieldInstance+    , mkEntitySinglePersistFieldInstance+    , mkPersistEntityInstance+    , mkEntityNeverNullInstance     ]   --runIO $ putStrLn $ pprint decs   return decs -mkEmbeddedDecs :: THEmbeddedDef -> Q [Dec]-mkEmbeddedDecs def = do+defaultMkEmbeddedDecs :: THEmbeddedDef -> Q [Dec]+defaultMkEmbeddedDecs def = do   --runIO (print def)-  decs <- fmap concat $ sequence-    [ mkEmbeddedPersistFieldInstance def-    , mkEmbeddedPurePersistFieldInstance def-    , mkEmbeddedInstance def+  decs <- fmap concat $ sequence $ map ($ def)+    [ mkEmbeddedPersistFieldInstance+    , mkEmbeddedPurePersistFieldInstance+    , mkEmbeddedInstance     ] --  runIO $ putStrLn $ pprint decs   return decs -mkPrimitiveDecs :: THPrimitiveDef -> Q [Dec]-mkPrimitiveDecs def = do+defaultMkPrimitiveDecs :: THPrimitiveDef -> Q [Dec]+defaultMkPrimitiveDecs def = do   --runIO (print def)-  decs <- fmap concat $ sequence-    [ mkPrimitivePersistFieldInstance def-    , mkPrimitivePrimitivePersistFieldInstance def+  decs <- fmap concat $ sequence $ map ($ def)+    [ mkPrimitivePersistFieldInstance+    , mkPrimitivePrimitivePersistFieldInstance     ] --  runIO $ putStrLn $ pprint decs   return decs
Database/Groundhog/TH/CodeGen.hs view
@@ -31,8 +31,9 @@ import Language.Haskell.TH.Syntax (Lift(..))
 import Control.Arrow (second)
 import Control.Monad (liftM, liftM2, forM, forM_, foldM, filterM, replicateM)
+import Data.Either (lefts, rights)
 import Data.List (findIndex, nub, partition)
-import Data.Maybe (catMaybes)
+import Data.Maybe (catMaybes, mapMaybe)
 
 mkEmbeddedPersistFieldInstance :: THEmbeddedDef -> Q [Dec]
 mkEmbeddedPersistFieldInstance def = do
@@ -81,6 +82,7 @@ 
   dbType' <- do
     v <- newName "v"
+    proxy <- newName "p"
     let mkField fNum f = do
         a <- newName "a"
         let fname = thDbFieldName f
@@ -88,10 +90,10 @@               then let pat = conP (thEmbeddedConstructorName def) $ replicate fNum wildP ++ [varP a] ++ replicate (length (thEmbeddedFields def) - fNum - 1) wildP
                    in caseE (varE v) $ [match pat (normalB $ varE a) []]
               else [| undefined :: $(return $ thFieldType f) |]
-            typ = mkType f nvar
+            typ = mkType f proxy nvar
         [| (fname, $typ) |]
     let pat = if null $ thEmbeddedTypeParams def then wildP else varP v
-    funD 'dbType $ [ clause [pat] (normalB [| DbEmbedded (EmbeddedDef False $(listE $ zipWith mkField [0..] $ thEmbeddedFields def)) Nothing |]) [] ]
+    funD 'dbType $ [ clause [varP proxy, pat] (normalB [| DbEmbedded (EmbeddedDef False $(listE $ zipWith mkField [0..] $ thEmbeddedFields def)) Nothing |]) [] ]
 
   let context = paramsContext (thEmbeddedTypeParams def) (thEmbeddedFields def)
   let decs = [persistName', toPersistValues', fromPersistValues', dbType']
@@ -197,10 +199,11 @@     toPersistValues' <- funD 'toPersistValues [clause [] (normalB [| primToPersistValue |]) []]
     fromPersistValues' <- funD 'fromPersistValues [clause [] (normalB [| primFromPersistValue |]) []]
     dbType' <- do
+      proxy <- newName "p"
       a <- newName "a"
-      let e = [| entityDef ((undefined :: Key v a -> v) $(varE a)) |]
-          body = [| DbTypePrimitive DbAutoKey False Nothing (Just (Left ($e, Nothing), Nothing, Nothing)) |]
-      funD 'dbType [clause [varP a] (normalB body) []]
+      let e = [| entityDef $(varE proxy) ((undefined :: Key v a -> v) $(varE a)) |]
+          body = [| DbTypePrimitive (getAutoKeyType $(varE proxy)) False Nothing (Just (Left ($e, Nothing), Nothing, Nothing)) |]
+      funD 'dbType [clause [varP proxy, varP a] (normalB body) []]
     
     let context = paramsContext (thTypeParams def) (thConstructors def >>= thConstrFields)
     let decs = [persistName', toPersistValues', fromPersistValues', dbType']
@@ -282,15 +285,16 @@ 
     dbType' <- do
       a <- newName "a"
+      proxy <- newName "p"
       let mkField f = do
           let fname = thDbFieldName f
               nvar = [| undefined :: $(return $ thFieldType f) |]
-              typ = mkType f nvar
+              typ = mkType f proxy nvar
           [| (fname, $typ) |]
       let embedded = [| EmbeddedDef False $(listE $ map mkField $ thUniqueKeyFields unique) |]
-          e = [| entityDef ((undefined :: Key v a -> v) $(varE a)) |]
+          e = [| entityDef $(varE proxy) ((undefined :: Key v a -> v) $(varE a)) |]
           body = [| DbEmbedded $embedded (Just (Left ($e, Just $(lift $ thUniqueKeyName unique)), Nothing, Nothing)) |]
-      funD 'dbType [clause [varP a] (normalB body) []]
+      funD 'dbType [clause [varP proxy, varP a] (normalB body) []]
     let context = paramsContext (thTypeParams def) (thConstructors def >>= thConstrFields)
     let decs = [persistName', toPersistValues', fromPersistValues', dbType']
     return $ InstanceD context (AppT (ConT ''PersistField) uniqKeyType) decs
@@ -302,9 +306,9 @@     uniqKeyType <- [t| Key $(return entity) (Unique $(conT $ mkName $ thUniqueKeyPhantomName unique)) |]
     let context = paramsContext (thTypeParams def) (thConstructors def >>= thConstrFields)
     let conName = mkName $ thUniqueKeyConstrName unique
-    isUniquePrim <- if (length $ thUniqueKeyFields unique) == 1
-      then isPrim $ thFieldType $ head $ thUniqueKeyFields unique
-      else return False
+    isUniquePrim <- case thUniqueKeyFields unique of
+      [uniq] -> isPrim $ thFieldType uniq
+      _      -> return False
     if isUniquePrim
       then do
         proxy <- newName "p"
@@ -322,11 +326,11 @@ mkKeyEqShowInstances :: THEntityDef -> Q [Dec]
 mkKeyEqShowInstances def = do
   let entity = foldl AppT (ConT (thDataName def)) $ map extractType $ thTypeParams def
-  let forKeys f = maybe [] (\k -> [f (thAutoKeyConstrName k) 1]) (thAutoKey def)
-               ++ map (\k -> f (thUniqueKeyConstrName k) (length $ thUniqueKeyFields k)) (thUniqueKeys def)
+  let keysInfo = maybe [] (\k -> [(thAutoKeyConstrName k, 1)]) (thAutoKey def)
+               ++ map (\k -> (thUniqueKeyConstrName k, length $ thUniqueKeyFields k)) (thUniqueKeys def)
 
   showsPrec' <- let
-    mkClause cName fieldsNum = do
+    mkClause (cName, fieldsNum) = do
       p <- newName "p"
       fields <- replicateM fieldsNum (newName "x")
       let pat = conP (mkName cName) $ map varP fields
@@ -334,22 +338,24 @@           showArgs = foldr1 (\a b -> [| $a . showString " " . $b |]) $ map (\a -> [| showsPrec 11 $(varE a) |]) fields
           body = [| showParen ($(varE p) >= (11 :: Int)) ($showC . $showArgs) |]
       clause [varP p, pat] (normalB body) []
-    in funD 'showsPrec $ forKeys mkClause
+    in funD 'showsPrec $ map mkClause keysInfo
     
   eq' <- let
-    mkClause cName fieldsNum = do
+    mkClause (cName, fieldsNum) = do
       let fields = replicateM fieldsNum (newName "x")
       (fields1, fields2) <- liftM2 (,) fields fields
       let mkPat = conP (mkName cName) . map varP
           body = foldr1 (\e1 e2 -> [| $e1 && $e2 |]) $ zipWith (\n1 n2 -> [| $(varE n1) == $(varE n2) |]) fields1 fields2
       clause [mkPat fields1, mkPat fields2] (normalB body) []
-    clauses = forKeys mkClause
+    clauses = map mkClause keysInfo
     noMatch = if length clauses > 1 then [clause [wildP, wildP] (normalB [| False |]) []] else []
     in funD '(==) $ clauses ++ noMatch
 
   let context = paramsContext (thTypeParams def) (thConstructors def >>= thConstrFields)
   typ <- [t| Key $(return entity) $(newName "a" >>= varT) |]
-  return $ [InstanceD context (AppT (ConT ''Eq) typ) [eq'], InstanceD context (AppT (ConT ''Show) typ) [showsPrec']]
+  return $ if null keysInfo
+    then []
+    else [InstanceD context (AppT (ConT ''Eq) typ) [eq'], InstanceD context (AppT (ConT ''Show) typ) [showsPrec']]
 
 mkEmbeddedInstance :: THEmbeddedDef -> Q [Dec]
 mkEmbeddedInstance def = do
@@ -415,26 +421,26 @@         keyDescr <- [t| BackendSpecific |]
         return [ForallC [] [EqualP (VarT uParam) keyDescr] $ NormalC (mkName $ thAutoKeyConstrName k) [(NotStrict, ConT ''PersistValue)]]
     uniques <- forM (thUniqueKeys def) $ \unique -> do
-      let cDef = head $ thConstructors def
       uniqType <- [t| Unique $(conT $ mkName $ thUniqueKeyPhantomName unique) |]
-      let uniqFieldNames = thUniqueFields $ findOne "unique" thUniqueKeyName thUniqueName unique $ thConstrUniques cDef
-      let uniqFields = concat $ flip map uniqFieldNames $ \name -> (filter ((== name) . thFieldName) $ thConstrFields cDef)
-      let uniqFields' = map (\f -> (NotStrict, thFieldType f)) uniqFields
+      let cDef = head $ thConstructors def
+          uniqFieldNames = lefts $ thUniqueFields $ findOne "unique" thUniqueName (thUniqueKeyName unique) $ thConstrUniques cDef
+          uniqFields = concat $ flip map uniqFieldNames $ \name -> (filter ((== name) . thFieldName) $ thConstrFields cDef)
+          uniqFields' = map (\f -> (NotStrict, thFieldType f)) uniqFields
       return $ ForallC [] [EqualP (VarT uParam) uniqType] $ NormalC (mkName $ thUniqueKeyConstrName unique) uniqFields'
     return $ DataInstD [] ''Key [entity, VarT uParam] (autoKey ++ uniques) []
 
   autoKey' <- do
     autoType <- case thAutoKey def of
-      Nothing -> conT ''()
+      Nothing -> [t| () |]
       Just _ -> [t| Key $(return entity) BackendSpecific |]
     return $ mkTySynInstD ''AutoKey [entity] autoType
     
   defaultKey' <- do
-    let keyType = case thAutoKey def of
-         Just k | thAutoKeyIsDef k -> [t| BackendSpecific |]
-         _ -> let unique = head $ filter thUniqueKeyIsDef $ thUniqueKeys def
-              in  [t| Unique $(conT $ mkName $ thUniqueKeyPhantomName unique) |]
-    typ <- [t| Key $(return entity) $keyType |]
+    typ <- case thAutoKey def of
+      Just k | thAutoKeyIsDef k -> [t| Key $(return entity) BackendSpecific |]
+      _ -> case filter thUniqueKeyIsDef $ thUniqueKeys def of
+        [unique] -> [t| Key $(return entity) (Unique $(conT $ mkName $ thUniqueKeyPhantomName unique)) |]
+        _ -> [t| () |]
     return $ mkTySynInstD ''DefaultKey [entity] typ
 
   isSumType' <- do
@@ -453,35 +459,35 @@     
   entityDef' <- do
     v <- newName "v"
+    proxy <- newName "p"
     let mkLambda t = [|undefined :: $(forallT (thTypeParams def) (cxt []) [t| $(return entity) -> $(return t) |]) |]
-    let types = map extractType $ thTypeParams def
-    let typeParams' = listE $ map (\t -> [| dbType ($(mkLambda t) $(varE v)) |]) types
-    let mkField c fNum f = do
-        a <- newName "a"
-        let fname = thDbFieldName f
-            nvar = if hasFreeVars (thFieldType f)
-              then let pat = conP (thConstrName c) $ replicate fNum wildP ++ [varP a] ++ replicate (length (thConstrFields c) - fNum - 1) wildP
-                       wildClause = if length (thConstructors def) > 1 then [match wildP (normalB [| undefined |]) []] else []
-                   in caseE (varE v) $ [match pat (normalB $ varE a) []] ++ wildClause
-              else [| undefined :: $(return $ thFieldType f) |]
-            typ = mkType f nvar
-        [| (fname, $typ) |]
-    let constrs = listE $ zipWith mkConstructorDef [0 :: Int ..] $ thConstructors def
-        mkConstructorDef cNum c@(THConstructorDef _ _ name keyName params conss) = [| ConstructorDef cNum name keyName $(listE $ map snd fields) $(listE $ map mkConstraint conss) |] where
+        types = map extractType $ thTypeParams def
+        typeParams' = listE $ map (\t -> [| dbType $(varE proxy) ($(mkLambda t) $(varE v)) |]) types
+        mkField c fNum f = do
+          a <- newName "a"
+          let fname = thDbFieldName f
+              nvar = if hasFreeVars (thFieldType f)
+                then let pat = conP (thConstrName c) $ replicate fNum wildP ++ [varP a] ++ replicate (length (thConstrFields c) - fNum - 1) wildP
+                         wildClause = if length (thConstructors def) > 1 then [match wildP (normalB [| undefined |]) []] else []
+                     in caseE (varE v) $ [match pat (normalB $ varE a) []] ++ wildClause
+                else [| undefined :: $(return $ thFieldType f) |]
+              typ = mkType f proxy nvar
+          [| (fname, $typ) |]
+        constrs = listE $ map mkConstructorDef $ thConstructors def
+        mkConstructorDef c@(THConstructorDef _ _ name keyName params conss) = [| ConstructorDef name keyName $(listE $ map snd fields) $(listE $ map mkConstraint conss) |] where
           fields = zipWith (\i f -> (thFieldName f, mkField c i f)) [0..] params
-          mkConstraint (THUniqueDef uName uType uFields) = [| UniqueDef uName uType $(listE $ map getField uFields) |]
-          getField fName = case lookup fName fields of
-            Just f -> f
-            Nothing -> error $ "Field name " ++ show fName ++ " declared in unique not found"
+          mkConstraint (THUniqueDef uName uType uFields) = [| UniqueDef (Just uName) uType $(listE $ map getField uFields) |]
+          getField (Left fName) = [| Left $(snd $ findOne "field" fst fName fields) |]
+          getField (Right expr) = [| Right expr |]
     
-    let paramNames = foldr1 (\p xs -> [| $p ++ [delim] ++ $xs |] ) $ map (\t -> [| persistName ($(mkLambda t) $(varE v)) |]) types
-    let fullEntityName = case null types of
+        paramNames = foldr1 (\p xs -> [| $p ++ [delim] ++ $xs |] ) $ map (\t -> [| persistName ($(mkLambda t) $(varE v)) |]) types
+        fullEntityName = case null types of
          True  -> [| $(stringE $ thDbEntityName def) |]
          False -> [| $(stringE $ thDbEntityName def) ++ [delim] ++ $(paramNames) |]
 
-    let body = normalB [| EntityDef $fullEntityName $(lift $ thEntitySchema def) $typeParams' $constrs |]
-    let pat = if null $ thTypeParams def then wildP else varP v
-    funD 'entityDef $ [ clause [pat] body [] ]
+        body = normalB [| EntityDef $fullEntityName $(lift $ thEntitySchema def) $typeParams' $constrs |]
+        entityPat = if null $ thTypeParams def then wildP else varP v
+    funD 'entityDef $ [ clause [varP proxy, entityPat] body [] ]
 
   toEntityPersistValues' <- liftM (FunD 'toEntityPersistValues) $ forM (zip [0 :: Int ..] $ thConstructors def) $ \(cNum, c) -> do
     vars <- mapM (\f -> newName "x" >>= \fname -> return (fname, thFieldType f)) $ thConstrFields c
@@ -504,7 +510,7 @@ 
   fromEntityPersistValues' <- do
       xs <- newName "xs"
-      let failureBody = normalB [| (\a -> fail (failMessage a $(varE xs)) >> return (a, [])) undefined |]
+      let failureBody = normalB [| (\a -> phantomDb >>= \proxy -> fail (failMessageNamed (entityName $ entityDef proxy a) $(varE xs)) >> return (a, [])) undefined |]
       failureName <- newName "failure"
       let failure = match wildP (normalB $ varE failureName) []
       matches <- forM (zip [0..] (thConstructors def)) $ \(cNum, c) -> do
@@ -522,15 +528,18 @@     mkClause cNum cdef | not (hasConstraints cdef) = clause [wildP, conP (thConstrName cdef) pats] (normalB [| (cNum, []) |]) [] where
       pats = map (const wildP) $ thConstrFields cdef
     mkClause cNum cdef = do
-      let allConstrainedFields = concatMap thUniqueFields $ thConstrUniques cdef
+      let allConstrainedFields = lefts $ concatMap thUniqueFields $ thConstrUniques cdef
       vars <- mapM (\f -> newName "x" >>= \x -> return $ if thFieldName f `elem` allConstrainedFields then Just (x, f) else Nothing) $ thConstrFields cdef
       proxy <- newName "p"
       let pat = conP (thConstrName cdef) $ map (maybe wildP (varP . fst)) vars
-          body = normalB $ [| (cNum, $(listE $ map mkUnique $ thConstrUniques cdef)) |]
-          mkUnique (THUniqueDef uName _ fnames) = [| (uName, $result) |] where
-            -- find corresponding field from vars
-            uFields = map (\f -> findOne "field" id (thFieldName . snd) f $ catMaybes vars) fnames
-            result = mkToPurePersistValues proxy $ map (second thFieldType) uFields
+          body = normalB $ [| (cNum, $(listE $ mapMaybe mkUnique $ thConstrUniques cdef)) |]
+          mkUnique (THUniqueDef uName _ fnames) = if null $ rights fnames
+            then let
+              -- find corresponding field from vars
+              uFields = map (\f -> findOne "field" (thFieldName . snd) f $ catMaybes vars) $ lefts fnames
+              result = mkToPurePersistValues proxy $ map (second thFieldType) uFields
+              in Just [| (uName, $result) |]
+            else Nothing
       clause [varP proxy, pat] body []
     in funD 'getUniques clauses
 
@@ -539,12 +548,13 @@     clauses = map mkClause thFieldNames
     mkClause f = do
         fArg <- newName "f"
+        proxy <- newName "p"
         let nvar = [| (undefined :: Field v c a -> a) $(varE fArg) |]
-            typ = mkType f nvar
+            typ = mkType f proxy nvar
             body = [| (($(lift $ thDbFieldName f), $typ), []) |]
-        clause [asP fArg $ conP (mkName $ thExprName f) []] (normalB body) []-    clauses' = if null clauses then [clause [wildP] (normalB [| undefined |]) []] else clauses-    in funD 'entityFieldChain clauses'+        clause [varP proxy, asP fArg $ conP (mkName $ thExprName f) []] (normalB body) []
+    clauses' = if null clauses then [clause [wildP] (normalB [| undefined |]) []] else clauses
+    in funD 'entityFieldChain clauses'
 
   let context = paramsContext (thTypeParams def) (thConstructors def >>= thConstrFields)
   let decs = [key', autoKey', defaultKey', isSumType', fields', entityDef', toEntityPersistValues', fromEntityPersistValues', getUniques', entityFieldChain']
@@ -566,69 +576,75 @@       letE stmts result
 
 mkEntityPersistFieldInstance :: THEntityDef -> Q [Dec]
-mkEntityPersistFieldInstance def = do
-  let types = map extractType $ thTypeParams def
-  let entity = foldl AppT (ConT (thDataName def)) types
+mkEntityPersistFieldInstance def = case getDefaultKey def of
+  Just defaultKey -> do
+    let types = map extractType $ thTypeParams def
+    let entity = foldl AppT (ConT (thDataName def)) types
   
-  persistName' <- do
-    v <- newName "v"
-    let mkLambda t = [|undefined :: $(forallT (thTypeParams def) (cxt []) [t| $(return entity) -> $(return t) |]) |]
+    persistName' <- do
+      v <- newName "v"
+      let mkLambda t = [|undefined :: $(forallT (thTypeParams def) (cxt []) [t| $(return entity) -> $(return t) |]) |]
     
-    let paramNames = foldr1 (\p xs -> [| $p ++ [delim] ++ $xs |] ) $ map (\t -> [| persistName ($(mkLambda t) $(varE v)) |]) types
-    let fullEntityName = case null types of
-         True  -> [| $(stringE $ thDbEntityName def) |]
-         False -> [| $(stringE $ thDbEntityName def) ++ [delim] ++ $(paramNames) |]
-    let body = normalB $ fullEntityName
-    let pat = if null types then wildP else varP v
-    funD 'persistName $ [ clause [pat] body [] ]
+      let paramNames = foldr1 (\p xs -> [| $p ++ [delim] ++ $xs |] ) $ map (\t -> [| persistName ($(mkLambda t) $(varE v)) |]) types
+      let fullEntityName = case null types of
+           True  -> [| $(stringE $ thDbEntityName def) |]
+           False -> [| $(stringE $ thDbEntityName def) ++ [delim] ++ $(paramNames) |]
+      let body = normalB $ fullEntityName
+      let pat = if null types then wildP else varP v
+      funD 'persistName $ [ clause [pat] body [] ]
   
-  isOne <- isDefaultKeyOneColumn def
-  let uniqInfo = either auto uniq $ getDefaultKey def where
-      auto _ = Nothing
-      uniq u = let name = mkName $ thUniqueKeyPhantomName u in Just $ (conT name, conE name)
+    isOne <- isDefaultKeyOneColumn def
+    let uniqInfo = either auto uniq defaultKey where
+        auto _ = Nothing
+        uniq u = let name = mkName $ thUniqueKeyPhantomName u in Just $ (conT name, conE name)
 
-  toPersistValues' <- do
-    let body = normalB $ case uniqInfo of
-         _ | isOne -> [| singleToPersistValue |]
-         Just u    -> [| toPersistValuesUnique $(snd u) |]
-         _         -> error "mkEntityPersistFieldInstance: key has no unique type"
-    funD 'toPersistValues $ [ clause [] body [] ]
+    toPersistValues' <- do
+      let body = normalB $ case uniqInfo of
+           _ | isOne -> [| singleToPersistValue |]
+           Just u    -> [| toPersistValuesUnique $(snd u) |]
+           _         -> error "mkEntityPersistFieldInstance: key has no unique type"
+      funD 'toPersistValues $ [ clause [] body [] ]
 
-  fromPersistValues' <- do
-    let body = normalB $ case uniqInfo of
-         _ | isOne -> [| singleFromPersistValue |]
-         Just u    -> [| fromPersistValuesUnique $(snd u) |]
-         _         -> error "mkEntityPersistFieldInstance: key has no unique type"
-    funD 'fromPersistValues $ [ clause [] body []]
+    fromPersistValues' <- do
+      let body = normalB $ case uniqInfo of
+           _ | isOne -> [| singleFromPersistValue |]
+           Just u    -> [| fromPersistValuesUnique $(snd u) |]
+           _         -> error "mkEntityPersistFieldInstance: key has no unique type"
+      funD 'fromPersistValues $ [ clause [] body []]
 
-  dbType' <- funD 'dbType $ [clause [] (normalB [| dbType . (undefined :: a -> DefaultKey a) |]) []]
+    dbType' <- do
+      proxy <- newName "p"
+      let body = [| dbType $(varE proxy) . (undefined :: a -> DefaultKey a) |]
+      funD 'dbType $ [clause [varP proxy] (normalB body) []]
 
-  let context = paramsContext (thTypeParams def) (thConstructors def >>= thConstrFields)
-  let decs = [persistName', toPersistValues', fromPersistValues', dbType']
-  return $ [InstanceD context (AppT (ConT ''PersistField) entity) decs]
+    let context = paramsContext (thTypeParams def) (thConstructors def >>= thConstrFields)
+    let decs = [persistName', toPersistValues', fromPersistValues', dbType']
+    return $ [InstanceD context (AppT (ConT ''PersistField) entity) decs]
+  Nothing -> return []
 
 mkEntitySinglePersistFieldInstance :: THEntityDef -> Q [Dec]
-mkEntitySinglePersistFieldInstance def = isDefaultKeyOneColumn def >>= \isOne ->
-  if isOne
-    then do
-      toSinglePersistValue' <- funD 'toSinglePersistValue $ [ clause [] (normalB to) [] ]
-      fromSinglePersistValue' <- funD 'fromSinglePersistValue $ [ clause [] (normalB from) []]
-      let decs = [toSinglePersistValue', fromSinglePersistValue']
-      return [InstanceD context (AppT (ConT ''SinglePersistField) entity) decs]
-    else return [] where
-    (to, from) = case getDefaultKey def of
-      Left  _ -> ([| toSinglePersistValueAutoKey |], [| fromSinglePersistValueAutoKey |])
-      Right k -> ([| toSinglePersistValueUnique $u |], [| fromSinglePersistValueUnique $u |]) where
-        u = conE $ mkName $ thUniqueKeyPhantomName k
-    types = map extractType $ thTypeParams def
-    entity = foldl AppT (ConT (thDataName def)) types
-    context = paramsContext (thTypeParams def) (thConstructors def >>= thConstrFields)
+mkEntitySinglePersistFieldInstance def = isDefaultKeyOneColumn def >>= \isOne -> case getDefaultKey def of
+  Just defaultKey | isOne -> do
+    let types = map extractType $ thTypeParams def
+        entity = foldl AppT (ConT (thDataName def)) types
+        context = paramsContext (thTypeParams def) (thConstructors def >>= thConstrFields)
 
+        (to, from) = case defaultKey of
+          Left  _ -> ([| toSinglePersistValueAutoKey |], [| fromSinglePersistValueAutoKey |])
+          Right k -> ([| toSinglePersistValueUnique $u |], [| fromSinglePersistValueUnique $u |]) where
+            u = conE $ mkName $ thUniqueKeyPhantomName k
+
+    toSinglePersistValue' <- funD 'toSinglePersistValue $ [ clause [] (normalB to) [] ]
+    fromSinglePersistValue' <- funD 'fromSinglePersistValue $ [ clause [] (normalB from) []]
+    let decs = [toSinglePersistValue', fromSinglePersistValue']
+    return [InstanceD context (AppT (ConT ''SinglePersistField) entity) decs]
+  _ -> return []
+
 mkEntityNeverNullInstance :: THEntityDef -> Q [Dec]
 mkEntityNeverNullInstance def = do
   let types = map extractType $ thTypeParams def
-  let entity = foldl AppT (ConT (thDataName def)) types
-  let context = paramsContext (thTypeParams def) (thConstructors def >>= thConstrFields)
+      entity = foldl AppT (ConT (thDataName def)) types
+      context = paramsContext (thTypeParams def) (thConstructors def >>= thConstrFields)
   isOne <- isDefaultKeyOneColumn def
   return $ if isOne
     then [InstanceD context (AppT (ConT ''NeverNull) entity) []]
@@ -646,7 +662,7 @@     let typ = if thPrimitiveStringEnumRepresentation def
           then [| DbTypePrimitive DbString False Nothing Nothing |]
           else [| DbTypePrimitive DbInt32  False Nothing Nothing |]
-    funD 'dbType $ [ clause [wildP] (normalB typ) [] ]
+    funD 'dbType $ [ clause [wildP, wildP] (normalB typ) [] ]
   let decs = [persistName', toPersistValues', fromPersistValues', dbType']
   return [ InstanceD [] (AppT (ConT ''PersistField) prim) decs
          , InstanceD [] (AppT (ConT ''NeverNull) prim) []
@@ -687,15 +703,18 @@   return [sig, func]
 
 isDefaultKeyOneColumn :: THEntityDef -> Q Bool
-isDefaultKeyOneColumn def = either (const $ return True) checkUnique $ getDefaultKey def where
-  checkUnique unique = if (length $ thUniqueKeyFields unique) == 1
-    then isPrim $ thFieldType $ head $ thUniqueKeyFields unique
-    else return False
+isDefaultKeyOneColumn def = case getDefaultKey def of
+  Just (Left _) -> return True
+  Just (Right unique) | (length $ thUniqueKeyFields unique) == 1 ->
+    isPrim $ thFieldType $ head $ thUniqueKeyFields unique
+  _ -> return False
 
-getDefaultKey :: THEntityDef -> Either THAutoKeyDef THUniqueKeyDef
+getDefaultKey :: THEntityDef -> Maybe (Either THAutoKeyDef THUniqueKeyDef)
 getDefaultKey def = case thAutoKey def of
-  Just k | thAutoKeyIsDef k -> Left k
-  _ -> Right $ head $ filter thUniqueKeyIsDef $ thUniqueKeys def
+  Just k | thAutoKeyIsDef k -> Just $ Left k
+  _ -> case filter thUniqueKeyIsDef $ thUniqueKeys def of
+    [] -> Nothing
+    (u:_) -> Just $ Right u
 
 paramsContext :: [TyVarBndr] -> [THFieldDef] -> Cxt
 paramsContext types fields = classPred ''PersistField params ++ classPred ''SinglePersistField maybys ++ classPred ''NeverNull maybys where
@@ -775,10 +794,10 @@         return (x:ys, zs)
       else return ([], x:xs)
 
-mkType :: THFieldDef -> ExpQ -> ExpQ
-mkType THFieldDef{..} nvar = t2 where
+mkType :: THFieldDef -> Name -> ExpQ -> ExpQ
+mkType THFieldDef{..} proxy nvar = t2 where
   psField = PSFieldDef thFieldName (Just thDbFieldName) thDbTypeName (Just thExprName) thEmbeddedDef thDefaultValue thReferenceParent
-  t1 = [| dbType $nvar |]
+  t1 = [| dbType $(varE proxy) $nvar |]
   -- if there are any type settings, apply them in runtime
   t2 = case (thDbTypeName, thEmbeddedDef, thDefaultValue, thReferenceParent) of
     (Nothing, Nothing, Nothing, Nothing) -> t1
Database/Groundhog/TH/Settings.hs view
@@ -27,8 +27,9 @@ import Language.Haskell.TH
 import Language.Haskell.TH.Syntax (Lift(..))
 import Control.Applicative
-import Control.Monad (mzero)
-import Data.Yaml
+import Control.Monad (forM, mzero)
+import Data.Aeson
+import qualified Data.HashMap.Strict as H
 
 data PersistDefinitions = PersistDefinitions {definitions :: [PersistDefinition]} deriving Show
 data PersistDefinition = PSEntityDef' PSEntityDef
@@ -82,7 +83,7 @@   , thDbTypeName :: Maybe String -- inet, NUMERIC(5, 2), VARCHAR(50)
   , thExprName :: String -- BarField
   , thFieldType :: Type
-  , thEmbeddedDef :: Maybe [PSFieldDef]
+  , thEmbeddedDef :: Maybe [PSFieldDef String]
   , thDefaultValue :: Maybe String
   , thReferenceParent :: Maybe (Maybe (Maybe String, String, [String]), Maybe ReferenceActionType, Maybe ReferenceActionType)
 } deriving Show
@@ -90,7 +91,7 @@ data THUniqueDef = THUniqueDef {
     thUniqueName :: String
   , thUniqueType :: UniqueType
-  , thUniqueFields :: [String] -- ^ Names of fields, not column names, i.e, thFieldName
+  , thUniqueFields :: [Either String String] -- ^ Either name of field, i.e, thFieldName, or expression
 } deriving Show
 
 data THUniqueKeyDef = THUniqueKeyDef {
@@ -116,7 +117,7 @@ data PSEmbeddedDef = PSEmbeddedDef {
     psEmbeddedName :: String
   , psDbEmbeddedName :: Maybe String -- ^ It is used only to set polymorphic part of name of its container
-  , psEmbeddedFields :: Maybe [PSFieldDef]
+  , psEmbeddedFields :: Maybe [PSFieldDef String]
 } deriving Show
 
 data PSPrimitiveDef = PSPrimitiveDef {
@@ -130,14 +131,14 @@   , psPhantomConstrName :: Maybe String -- U2Constructor
   , psDbConstrName :: Maybe String -- SQLU2
   , psDbAutoKeyName :: Maybe (Maybe String) -- u2_id
-  , psConstrFields  :: Maybe [PSFieldDef]
+  , psConstrFields  :: Maybe [PSFieldDef String]
   , psConstrUniques :: Maybe [PSUniqueDef]
 } deriving Show
 
 data PSUniqueDef = PSUniqueDef {
     psUniqueName :: String
   , psUniqueType :: Maybe UniqueType
-  , psUniqueFields :: [String]
+  , psUniqueFields :: [Either String String]
 } deriving Show
 
 data PSUniqueKeyDef = PSUniqueKeyDef {
@@ -145,7 +146,7 @@   , psUniqueKeyPhantomName :: Maybe String
   , psUniqueKeyConstrName :: Maybe String
   , psUniqueKeyDbName :: Maybe String
-  , psUniqueKeyFields :: Maybe [PSFieldDef]
+  , psUniqueKeyFields :: Maybe [PSFieldDef String]
   , psUniqueKeyMakeEmbedded :: Maybe Bool
   , psUniqueKeyIsDef :: Maybe Bool
 } deriving Show
@@ -190,7 +191,7 @@   lift SetNull = [| SetNull |]
   lift SetDefault = [| SetDefault |]
 
-instance Lift PSFieldDef where
+instance Lift (PSFieldDef String) where
   lift (PSFieldDef {..}) = [| PSFieldDef $(lift psFieldName) $(lift psDbFieldName) $(lift psDbTypeName) $(lift psExprName) $(lift psEmbeddedDef) $(lift psDefaultValue) $(lift psReferenceParent) |]
 
 instance Lift PSUniqueKeyDef where
@@ -219,20 +220,23 @@     _ -> mzero
 
 instance FromJSON PersistDefinition where
-  parseJSON obj = PSEntityDef' <$> parseJSON obj
-              <|> PSEmbeddedDef' <$> parseJSON obj
-              <|> PSPrimitiveDef' <$> parseJSON obj
+  parseJSON obj@(Object v) = case () of
+    _ | H.member "entity" v -> PSEntityDef' <$> parseJSON obj
+    _ | H.member "embedded" v -> PSEmbeddedDef' <$> parseJSON obj
+    _ | H.member "primitive" v -> PSPrimitiveDef' <$> parseJSON obj
+    _ -> mzero
+  parseJSON _ = mzero
 
 instance FromJSON PSEntityDef where
-  parseJSON (Object v) = PSEntityDef <$> v .: "entity" <*> v .:? "dbName" <*> v .:? "schema" <*> optional (v .: "autoKey") <*> v .:? "keys" <*> v .:? "constructors"
-  parseJSON _          = mzero
+  parseJSON = withObject "entity" $ \v ->
+    PSEntityDef <$> v .: "entity" <*> v .:? "dbName" <*> v .:? "schema" <*> optional (v .: "autoKey") <*> v .:? "keys" <*> v .:? "constructors"
 
 instance FromJSON PSEmbeddedDef where
-  parseJSON (Object v) = PSEmbeddedDef <$> v .: "embedded" <*> v .:? "dbName" <*> v .:? "fields"
-  parseJSON _          = mzero
+  parseJSON = withObject "embedded" $ \v ->
+    PSEmbeddedDef <$> v .: "embedded" <*> v .:? "dbName" <*> v .:? "fields"
 
 instance FromJSON PSPrimitiveDef where
-  parseJSON (Object v) = do
+  parseJSON = withObject "primitive" $ \v -> do
     x <- v .:? "representation"
     let representation = case x of
           Nothing -> pure True
@@ -240,15 +244,18 @@           Just "enum" -> pure False
           Just r -> fail $ "parseJSON: representation expected [\"showread\",\"enum\"], but got " ++ r
     PSPrimitiveDef <$> v .: "primitive" <*> v .:? "dbName" <*> pure representation
-  parseJSON _          = mzero
 
 instance FromJSON PSConstructorDef where
-  parseJSON (Object v) = PSConstructorDef <$> v .: "name" <*> v .:? "phantomName" <*> v .:? "dbName" <*> v .:? "keyDbName" <*> v .:? "fields" <*> v .:? "uniques"
-  parseJSON _          = mzero
+  parseJSON = withObject "constructor" $ \v ->
+     PSConstructorDef <$> v .: "name" <*> v .:? "phantomName" <*> v .:? "dbName" <*> v .:? "keyDbName" <*> v .:? "fields" <*> v .:? "uniques"
 
 instance FromJSON PSUniqueDef where
-  parseJSON (Object v) = PSUniqueDef <$> v .: "name" <*> v .:? "type" <*> v .: "fields"
-  parseJSON _          = mzero
+  parseJSON = withObject "unique" $ \v -> do
+    fields <- v .: "fields"
+    fields' <- forM fields $ \f -> case f of
+      Object expr  -> Right <$> expr .: "expr"
+      field -> Left <$> parseJSON field
+    PSUniqueDef <$> v .: "name" <*> v .:? "type" <*> pure fields'
 
 instance FromJSON UniqueType where
   parseJSON o = do
@@ -266,9 +273,10 @@       Just a -> return a
       Nothing -> fail $ "parseJSON: UniqueType expected " ++ show (map fst vals) ++ ", but got " ++ x
 
-instance FromJSON PSFieldDef where
-  parseJSON (Object v) = PSFieldDef <$> v .: "name" <*> v .:? "dbName" <*> v .:? "type" <*> v .:? "exprName" <*> v .:? "embeddedType" <*> v .:? "default" <*> mkRefSettings where
-    mkRefSettings = do
+instance FromJSON (PSFieldDef String) where
+  parseJSON = withObject "field" $ \v ->
+    PSFieldDef <$> v .: "name" <*> v .:? "dbName" <*> v .:? "type" <*> v .:? "exprName" <*> v .:? "embeddedType" <*> v .:? "default" <*> mkRefSettings v where
+    mkRefSettings v = do
       ref <- v .:? "reference"
       (parent, onDel, onUpd) <- case ref of
         Just (Object r) -> (,,) <$> parentRef <*> r .:? "onDelete" <*> r .:? "onUpdate" where
@@ -279,12 +287,11 @@       pure $ case (parent, onDel <|> onDel', onUpd <|> onUpd') of
         (Nothing, Nothing, Nothing) -> Nothing
         refSettings -> Just refSettings
-  parseJSON _          = mzero
 
 instance FromJSON PSUniqueKeyDef where
-  parseJSON (Object v) = PSUniqueKeyDef <$> v .: "name" <*> v .:? "keyPhantom" <*> v .:? "constrName" <*> v .:? "dbName" <*> v .:? "fields" <*> v .:? "mkEmbedded" <*> v .:? "default"
-  parseJSON _          = mzero
+  parseJSON = withObject "unique key" $ \v ->
+    PSUniqueKeyDef <$> v .: "name" <*> v .:? "keyPhantom" <*> v .:? "constrName" <*> v .:? "dbName" <*> v .:? "fields" <*> v .:? "mkEmbedded" <*> v .:? "default"
 
 instance FromJSON PSAutoKeyDef where
-  parseJSON (Object v) = PSAutoKeyDef <$> v .:? "constrName" <*> v .:? "default"
-  parseJSON _          = mzero
+  parseJSON = withObject "autogenerated key" $ \v ->
+    PSAutoKeyDef <$> v .:? "constrName" <*> v .:? "default"
changelog view
@@ -1,3 +1,9 @@+0.6.0+* Entities without keys. It can be useful for many-to-many tables which hold keys but are not referenced+* Entity and fields descriptions are parameterized so that they can be promoted+* Entity and fields descriptions are dependent on database proxy. It allows to use different types depending on a database, for example, the same type can be array[] in PostgreSQL and varchar elsewhere+* Migration support for indexes on expressions+ 0.5.1 * Export firstChar for creating user naming styles * Support entities with no fields
groundhog-th.cabal view
@@ -1,5 +1,5 @@ name:            groundhog-th-version:         0.5.1+version:         0.6.0 license:         BSD3 license-file:    LICENSE author:          Boris Lykah <lykahb@gmail.com>@@ -17,10 +17,12 @@ library     build-depends:   base                     >= 4         && < 5                    , bytestring               >= 0.9-                   , groundhog                >= 0.5.0     && < 0.6.0+                   , groundhog                >= 0.6       && < 0.7                    , template-haskell                    , time                     >= 1.1.4                    , containers               >= 0.2+                   , unordered-containers     >= 0.1.3+                   , aeson                    >= 0.7                    , yaml                     >= 0.8.1                    , text                     >= 0.8     exposed-modules: Database.Groundhog.TH