packages feed

persistable-record 0.4.2.0 → 0.5.0.0

raw patch · 11 files changed

+427/−294 lines, 11 filesdep +ghc-primPVP ok

version bump matches the API change (PVP)

Dependencies added: ghc-prim

API changes (from Hackage documentation)

- Database.Record.FromSql: instance (Database.Record.FromSql.FromSql q a, Database.Record.FromSql.FromSql q b) => Database.Record.FromSql.FromSql q (a, b)
- Database.Record.Persistable: data PersistableRecordWidth a
- Database.Record.Persistable: instance (Database.Record.Persistable.PersistableWidth a, Database.Record.Persistable.PersistableWidth b) => Database.Record.Persistable.PersistableWidth (a, b)
- Database.Record.TH: definePersistableInstance :: TypeQ -> TypeQ -> VarName -> VarName -> Int -> Q [Dec]
- Database.Record.TH: defineRecordParser :: TypeQ -> VarName -> (TypeQ, ExpQ) -> Int -> Q [Dec]
- Database.Record.TH: defineRecordPrinter :: TypeQ -> VarName -> (TypeQ, ExpQ) -> Int -> Q [Dec]
- Database.Record.TH: derivingData :: Name
- Database.Record.TH: derivingEq :: Name
- Database.Record.TH: derivingRead :: Name
- Database.Record.TH: derivingShow :: Name
- Database.Record.TH: derivingTypeable :: Name
- Database.Record.TH: makeRecordPersistableWithSqlType :: TypeQ -> (VarName, VarName) -> (TypeQ, ExpQ) -> Int -> Q [Dec]
- Database.Record.TH: makeRecordPersistableWithSqlTypeDefault :: TypeQ -> String -> String -> Int -> Q [Dec]
- Database.Record.TH: makeRecordPersistableWithSqlTypeDefaultFromDefined :: TypeQ -> Name -> Q [Dec]
- Database.Record.TH: makeRecordPersistableWithSqlTypeFromDefined :: TypeQ -> (VarName, VarName) -> Name -> Q [Dec]
- Database.Record.TH: makeRecordPersistableWithSqlTypeWithConfig :: TypeQ -> NameConfig -> String -> String -> Int -> Q [Dec]
- Database.Record.TH: persistableFunctionNamesDefault :: Name -> (VarName, VarName)
- Database.Record.TH: recordType :: NameConfig -> String -> String -> TypeQ
- Database.Record.ToSql: instance (Database.Record.Persistable.PersistableType q, Database.Record.ToSql.ToSql q a) => Database.Record.ToSql.ToSql q (GHC.Base.Maybe a)
- Database.Record.ToSql: instance (Database.Record.ToSql.ToSql q a, Database.Record.ToSql.ToSql q b) => Database.Record.ToSql.ToSql q (a, b)
+ Database.Record.FromSql: instance (Database.Record.FromSql.GFromSql q a, Database.Record.FromSql.GFromSql q b) => Database.Record.FromSql.GFromSql q (a GHC.Generics.:*: b)
+ Database.Record.FromSql: instance Database.Record.FromSql.FromSql q a => Database.Record.FromSql.GFromSql q (GHC.Generics.K1 i a)
+ Database.Record.FromSql: instance Database.Record.FromSql.GFromSql q GHC.Generics.U1
+ Database.Record.FromSql: instance Database.Record.FromSql.GFromSql q a => Database.Record.FromSql.GFromSql q (GHC.Generics.M1 i c a)
+ Database.Record.Persistable: class GFieldWidthList f
+ Database.Record.Persistable: data ProductConst a b
+ Database.Record.Persistable: genericFieldOffsets :: (Generic a, GFieldWidthList (Rep a)) => ProductConst (Array Int Int) a
+ Database.Record.Persistable: getProductConst :: ProductConst a b -> a
+ Database.Record.Persistable: instance (Database.Record.Persistable.GFieldWidthList a, Database.Record.Persistable.GFieldWidthList b) => Database.Record.Persistable.GFieldWidthList (a GHC.Generics.:*: b)
+ Database.Record.Persistable: instance Database.Record.Persistable.GFieldWidthList GHC.Generics.U1
+ Database.Record.Persistable: instance Database.Record.Persistable.GFieldWidthList a => Database.Record.Persistable.GFieldWidthList (GHC.Generics.M1 i c a)
+ Database.Record.Persistable: instance Database.Record.Persistable.PersistableWidth a => Database.Record.Persistable.GFieldWidthList (GHC.Generics.K1 i a)
+ Database.Record.Persistable: instance GHC.Show.Show a => GHC.Show.Show (Database.Record.Persistable.ProductConst a b)
+ Database.Record.Persistable: type PersistableRecordWidth a = ProductConst (Sum Int) a
+ Database.Record.TH: defineTupleInstances :: Int -> Q [Dec]
+ Database.Record.TH: recordTemplate :: NameConfig -> String -> String -> (TypeQ, ExpQ)
+ Database.Record.ToSql: instance (Database.Record.Persistable.PersistableType q, Database.Record.Persistable.PersistableWidth a, Database.Record.ToSql.ToSql q a) => Database.Record.ToSql.ToSql q (GHC.Base.Maybe a)
+ Database.Record.ToSql: instance (Database.Record.ToSql.GToSql q a, Database.Record.ToSql.GToSql q b) => Database.Record.ToSql.GToSql q (a GHC.Generics.:*: b)
+ Database.Record.ToSql: instance Database.Record.ToSql.GToSql q GHC.Generics.U1
+ Database.Record.ToSql: instance Database.Record.ToSql.GToSql q a => Database.Record.ToSql.GToSql q (GHC.Generics.M1 i c a)
+ Database.Record.ToSql: instance Database.Record.ToSql.ToSql q a => Database.Record.ToSql.GToSql q (GHC.Generics.K1 i a)
+ Database.Record.TupleInstances: instance (Database.Record.FromSql.FromSql q a1, Database.Record.FromSql.FromSql q a2) => Database.Record.FromSql.FromSql q (a1, a2)
+ Database.Record.TupleInstances: instance (Database.Record.FromSql.FromSql q a1, Database.Record.FromSql.FromSql q a2, Database.Record.FromSql.FromSql q a3) => Database.Record.FromSql.FromSql q (a1, a2, a3)
+ Database.Record.TupleInstances: instance (Database.Record.FromSql.FromSql q a1, Database.Record.FromSql.FromSql q a2, Database.Record.FromSql.FromSql q a3, Database.Record.FromSql.FromSql q a4) => Database.Record.FromSql.FromSql q (a1, a2, a3, a4)
+ Database.Record.TupleInstances: instance (Database.Record.FromSql.FromSql q a1, Database.Record.FromSql.FromSql q a2, Database.Record.FromSql.FromSql q a3, Database.Record.FromSql.FromSql q a4, Database.Record.FromSql.FromSql q a5) => Database.Record.FromSql.FromSql q (a1, a2, a3, a4, a5)
+ Database.Record.TupleInstances: instance (Database.Record.FromSql.FromSql q a1, Database.Record.FromSql.FromSql q a2, Database.Record.FromSql.FromSql q a3, Database.Record.FromSql.FromSql q a4, Database.Record.FromSql.FromSql q a5, Database.Record.FromSql.FromSql q a6) => Database.Record.FromSql.FromSql q (a1, a2, a3, a4, a5, a6)
+ Database.Record.TupleInstances: instance (Database.Record.FromSql.FromSql q a1, Database.Record.FromSql.FromSql q a2, Database.Record.FromSql.FromSql q a3, Database.Record.FromSql.FromSql q a4, Database.Record.FromSql.FromSql q a5, Database.Record.FromSql.FromSql q a6, Database.Record.FromSql.FromSql q a7) => Database.Record.FromSql.FromSql q (a1, a2, a3, a4, a5, a6, a7)
+ Database.Record.TupleInstances: instance (Database.Record.Persistable.PersistableWidth a1, Database.Record.Persistable.PersistableWidth a2) => Database.Record.Persistable.PersistableWidth (a1, a2)
+ Database.Record.TupleInstances: instance (Database.Record.Persistable.PersistableWidth a1, Database.Record.Persistable.PersistableWidth a2, Database.Record.Persistable.PersistableWidth a3) => Database.Record.Persistable.PersistableWidth (a1, a2, a3)
+ Database.Record.TupleInstances: instance (Database.Record.Persistable.PersistableWidth a1, Database.Record.Persistable.PersistableWidth a2, Database.Record.Persistable.PersistableWidth a3, Database.Record.Persistable.PersistableWidth a4) => Database.Record.Persistable.PersistableWidth (a1, a2, a3, a4)
+ Database.Record.TupleInstances: instance (Database.Record.Persistable.PersistableWidth a1, Database.Record.Persistable.PersistableWidth a2, Database.Record.Persistable.PersistableWidth a3, Database.Record.Persistable.PersistableWidth a4, Database.Record.Persistable.PersistableWidth a5) => Database.Record.Persistable.PersistableWidth (a1, a2, a3, a4, a5)
+ Database.Record.TupleInstances: instance (Database.Record.Persistable.PersistableWidth a1, Database.Record.Persistable.PersistableWidth a2, Database.Record.Persistable.PersistableWidth a3, Database.Record.Persistable.PersistableWidth a4, Database.Record.Persistable.PersistableWidth a5, Database.Record.Persistable.PersistableWidth a6) => Database.Record.Persistable.PersistableWidth (a1, a2, a3, a4, a5, a6)
+ Database.Record.TupleInstances: instance (Database.Record.Persistable.PersistableWidth a1, Database.Record.Persistable.PersistableWidth a2, Database.Record.Persistable.PersistableWidth a3, Database.Record.Persistable.PersistableWidth a4, Database.Record.Persistable.PersistableWidth a5, Database.Record.Persistable.PersistableWidth a6, Database.Record.Persistable.PersistableWidth a7) => Database.Record.Persistable.PersistableWidth (a1, a2, a3, a4, a5, a6, a7)
+ Database.Record.TupleInstances: instance (Database.Record.ToSql.ToSql q a1, Database.Record.ToSql.ToSql q a2) => Database.Record.ToSql.ToSql q (a1, a2)
+ Database.Record.TupleInstances: instance (Database.Record.ToSql.ToSql q a1, Database.Record.ToSql.ToSql q a2, Database.Record.ToSql.ToSql q a3) => Database.Record.ToSql.ToSql q (a1, a2, a3)
+ Database.Record.TupleInstances: instance (Database.Record.ToSql.ToSql q a1, Database.Record.ToSql.ToSql q a2, Database.Record.ToSql.ToSql q a3, Database.Record.ToSql.ToSql q a4) => Database.Record.ToSql.ToSql q (a1, a2, a3, a4)
+ Database.Record.TupleInstances: instance (Database.Record.ToSql.ToSql q a1, Database.Record.ToSql.ToSql q a2, Database.Record.ToSql.ToSql q a3, Database.Record.ToSql.ToSql q a4, Database.Record.ToSql.ToSql q a5) => Database.Record.ToSql.ToSql q (a1, a2, a3, a4, a5)
+ Database.Record.TupleInstances: instance (Database.Record.ToSql.ToSql q a1, Database.Record.ToSql.ToSql q a2, Database.Record.ToSql.ToSql q a3, Database.Record.ToSql.ToSql q a4, Database.Record.ToSql.ToSql q a5, Database.Record.ToSql.ToSql q a6) => Database.Record.ToSql.ToSql q (a1, a2, a3, a4, a5, a6)
+ Database.Record.TupleInstances: instance (Database.Record.ToSql.ToSql q a1, Database.Record.ToSql.ToSql q a2, Database.Record.ToSql.ToSql q a3, Database.Record.ToSql.ToSql q a4, Database.Record.ToSql.ToSql q a5, Database.Record.ToSql.ToSql q a6, Database.Record.ToSql.ToSql q a7) => Database.Record.ToSql.ToSql q (a1, a2, a3, a4, a5, a6, a7)
- Database.Record.FromSql: class FromSql q a
+ Database.Record.FromSql: class FromSql q a where recordFromSql = to <$> gFromSql
- Database.Record.FromSql: recordFromSql :: FromSql q a => RecordFromSql q a
+ Database.Record.FromSql: recordFromSql :: (FromSql q a, Generic a, GFromSql q (Rep a)) => RecordFromSql q a
- Database.Record.Persistable: class PersistableWidth a
+ Database.Record.Persistable: class PersistableWidth a where persistableWidth = pmapConst (Sum . lastA) genericFieldOffsets where lastA a = a ! (snd $ bounds a)
- Database.Record.Persistable: persistableWidth :: PersistableWidth a => PersistableRecordWidth a
+ Database.Record.Persistable: persistableWidth :: (PersistableWidth a, Generic a, GFieldWidthList (Rep a)) => PersistableRecordWidth a
- Database.Record.TH: defineRecord :: TypeQ -> (VarName, VarName) -> ConName -> [(VarName, TypeQ)] -> [Name] -> Q [Dec]
+ Database.Record.TH: defineRecord :: TypeQ -> ConName -> [(VarName, TypeQ)] -> [Name] -> Q [Dec]
- Database.Record.ToSql: class PersistableWidth a => ToSql q a
+ Database.Record.ToSql: class ToSql q a where recordToSql = from `mapToSql` gToSql
- Database.Record.ToSql: recordToSql :: ToSql q a => RecordToSql q a
+ Database.Record.ToSql: recordToSql :: (ToSql q a, Generic a, GToSql q (Rep a)) => RecordToSql q a

Files

+ ChangeLog.md view
@@ -0,0 +1,38 @@+<!-- -*- Markdown -*- -->++## 0.5.0.0++- Add generic instances of FromSql, ToSql and PersistableWidth.++## 0.4.1.1++- Tested with GHC 8.0.2+- Add a small test set.++## 0.4.1.0++- Export columnName of NameConfig.++## 0.4.0.3++- Drop an unreferenced definition.++## 0.4.0.2++- Add tested-with.++## 0.4.0.1++- Apply th-data-compat.++## 0.4.0.0++- Divide PersistableValue interface to FromSql and ToSql.++## 0.3.0.0++- Add symbol name configurations of templates.++## 0.2.0.0++- TH quotation of derive class names.
persistable-record.cabal view
@@ -1,5 +1,5 @@ name:                persistable-record-version:             0.4.2.0+version:             0.5.0.0 synopsis:            Binding between SQL database values and haskell records. description:         This package contiains types to represent table constraints and                      interfaces to bind between SQL database values and Haskell records.@@ -12,23 +12,31 @@ category:            Database build-type:          Simple cabal-version:       >=1.10-tested-with:           GHC == 8.2.1-                     , GHC == 8.0.1, GHC == 8.0.2+tested-with:           GHC == 8.0.1, GHC == 8.0.2                      , GHC == 7.10.1, GHC == 7.10.2, GHC == 7.10.3                      , GHC == 7.8.1, GHC == 7.8.2, GHC == 7.8.3, GHC == 7.8.4                      , GHC == 7.6.1, GHC == 7.6.2, GHC == 7.6.3                      , GHC == 7.4.1, GHC == 7.4.2+extra-source-files:  ChangeLog.md +flag ghc74-generic+  description: Enable for generic programming using old ghc. If true use ghc-prim.+  default:     False+ library   exposed-modules:                         Database.Record.FromSql                         Database.Record.ToSql                         Database.Record.Persistable+                        Database.Record.TupleInstances                         Database.Record.Instances                         Database.Record.KeyConstraint                         Database.Record                         Database.Record.TH +  other-modules:+                        Database.Record.InternalTH+   build-depends:          base <5                         , template-haskell                         , th-data-compat@@ -37,6 +45,9 @@                         , transformers                         , dlist                         , names-th+  if flag(ghc74-generic)+    build-depends:        ghc-prim == 0.2.*+   hs-source-dirs:       src   ghc-options:          -Wall   default-language:     Haskell2010@@ -45,9 +56,12 @@   build-depends:          base <5                         , quickcheck-simple                         , persistable-record+  if flag(ghc74-generic)+    build-depends:        ghc-prim == 0.2.*    type:                 exitcode-stdio-1.0   main-is:              nestedEq.hs+  other-modules:        Model   hs-source-dirs:       test   ghc-options:          -Wall   default-language:     Haskell2010
src/Database/Record.hs view
@@ -48,6 +48,7 @@   (ToSqlM, RecordToSql, ToSql(..), valueRecordToSql,    runFromRecord, wrapToSql, putRecord, putEmpty, fromRecord,    updateValuesByUnique, updateValuesByPrimary)+import Database.Record.TupleInstances ()  {- $concepts On most drivers for SQL database,
src/Database/Record/FromSql.hs view
@@ -1,10 +1,12 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE DefaultSignatures #-}  -- | -- Module      : Database.Record.FromSql--- Copyright   : 2013 Kei Hibino+-- Copyright   : 2013-2017 Kei Hibino -- License     : BSD3 -- -- Maintainer  : ex8k.hibino@gmail.com@@ -29,13 +31,15 @@   valueRecordFromSql,   ) where +import GHC.Generics (Generic, Rep, U1 (..), K1 (..), M1 (..), (:*:)(..), to)+import Control.Applicative ((<$>), Applicative (pure, (<*>)))+import Control.Monad (liftM, ap)+ import Database.Record.Persistable (PersistableType) import qualified Database.Record.Persistable as Persistable import Database.Record.KeyConstraint   (HasColumnConstraint(columnConstraint), ColumnConstraint, NotNull, index) -import Control.Monad (liftM, ap)-import Control.Applicative ((<$>), Applicative(pure, (<*>)))  {- $recordFromSql Structure of 'RecordFromSql' 'q' 'a' is similar to parser.@@ -128,11 +132,26 @@   -- | 'RecordFromSql' proof object.   recordFromSql :: RecordFromSql q a --- | Inference rule of 'RecordFromSql' proof object which can convert---   from list of SQL type ['q'] into Haskell tuple ('a', 'b') type.-instance (FromSql q a, FromSql q b) => FromSql q (a, b)  where-  recordFromSql = recordFromSql <&> recordFromSql+  default recordFromSql :: (Generic a, GFromSql q (Rep a)) => RecordFromSql q a+  recordFromSql = to <$> gFromSql ++class GFromSql q f where+  gFromSql :: RecordFromSql q (f a)++instance GFromSql q U1 where+  gFromSql = createRecordFromSql $ (,) U1++instance (GFromSql q a, GFromSql q b) => GFromSql q (a :*: b) where+  gFromSql = (:*:) <$> gFromSql <*> gFromSql++instance GFromSql q a => GFromSql q (M1 i c a) where+  gFromSql = M1 <$> gFromSql++instance FromSql q a => GFromSql q (K1 i a) where+  gFromSql = K1 <$> recordFromSql++ -- | Inference rule of 'RecordFromSql' proof object which can convert --   from list of SQL type ['q'] into Haskell 'Maybe' type. instance (HasColumnConstraint NotNull a, FromSql q a, PersistableType q)@@ -141,8 +160,7 @@  -- | Inference rule of 'RecordFromSql' proof object which can convert --   from /empty/ list of SQL type ['q'] into Haskell unit () type.-instance FromSql q () where-  recordFromSql = RecordFromSql (\qs -> ((), qs))+instance FromSql q ()  -- default generic instance  -- | Run inferred 'RecordFromSql' proof object. --   Convert from list of SQL type ['q'] into haskell type 'a' and rest of list ['q'].
+ src/Database/Record/InternalTH.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE ConstraintKinds #-}++module Database.Record.InternalTH (+  defineTupleInstances+  ) where++import Control.Applicative ((<$>))+import Data.List (foldl')+import Language.Haskell.TH+  (Q, mkName, Name,+   conT, varT, tupleT, appT, classP,+   Dec, instanceD, )++import Database.Record.Persistable (PersistableWidth)+import Database.Record.FromSql (FromSql)+import Database.Record.ToSql (ToSql)+++persistableWidth :: Int -> Q [Dec]+persistableWidth n = do+  let vs = [ varT . mkName $ "a" ++ show i | i <- [1 .. n] ]+  (:[]) <$> instanceD+    -- in template-haskell 2.8 or older, Pred is not Type+    (mapM (classP ''PersistableWidth . (:[])) vs)+    [t| PersistableWidth $(foldl' appT (tupleT n) vs) |]+    []++tupleInstance2 :: Int -> Name -> Q [Dec]+tupleInstance2 n clazz = do+  let vs = [ varT . mkName $ "a" ++ show i | i <- [1 .. n] ]+      q = varT $ mkName "q"+  (:[]) <$> instanceD+    -- in template-haskell 2.8 or older, Pred is not Type+    (mapM (\v -> classP clazz [q, v]) vs)+    [t| $(conT clazz) $q $(foldl' appT (tupleT n) vs) |]+    []++-- | Template to define tuple instances of persistable-record classes.+defineTupleInstances :: Int -> Q [Dec]+defineTupleInstances n =+  concat <$> sequence+  [ persistableWidth n+  , tupleInstance2 n ''FromSql+  , tupleInstance2 n ''ToSql ]
src/Database/Record/Persistable.hs view
@@ -1,9 +1,12 @@+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE DefaultSignatures #-}  -- | -- Module      : Database.Record.Persistable--- Copyright   : 2013 Kei Hibino+-- Copyright   : 2013-2017 Kei Hibino -- License     : BSD3 -- -- Maintainer  : ex8k.hibino@gmail.com@@ -21,12 +24,23 @@   unsafePersistableRecordWidth, unsafeValueWidth, (<&>), maybeWidth,    -- * Inference rules for proof objects-   PersistableType(..), sqlNullValue,-  PersistableWidth (..), derivedWidth+  PersistableWidth (..), derivedWidth,++  -- * low-level interfaces+  GFieldWidthList,+  ProductConst, getProductConst,+  genericFieldOffsets,   ) where +import GHC.Generics (Generic, Rep, U1 (..), K1 (..), M1 (..), (:*:)(..), to)+import Control.Applicative ((<$>), pure, (<*>), Const (..))+import Data.Monoid (Monoid, Sum (..))+import Data.Array (Array, listArray, bounds, (!))+import Data.DList (DList)+import qualified Data.DList as DList + -- | Proof object to specify type 'q' is SQL type newtype PersistableSqlType q = PersistableSqlType q @@ -40,35 +54,54 @@ unsafePersistableSqlTypeFromNull =  PersistableSqlType  +-- | Restricted in product isomorphism record type b+newtype ProductConst a b =+  ProductConst { unPC :: Const a b }++-- | extract constant value of 'ProductConst'.+getProductConst :: ProductConst a b -> a+getProductConst = getConst . unPC+{-# INLINE getProductConst #-}+ -- | Proof object to specify width of Haskell type 'a' --   when converting to SQL type list.-newtype PersistableRecordWidth a =-  PersistableRecordWidth Int+type PersistableRecordWidth a = ProductConst (Sum Int) a +-- unsafely map PersistableRecordWidth+pmap :: Monoid e => (a -> b) -> ProductConst e a -> ProductConst e b+f `pmap` prw = ProductConst $ f <$> unPC prw++-- unsafely ap PersistableRecordWidth+pap :: Monoid e => ProductConst e (a -> b) -> ProductConst e a -> ProductConst e b+wf `pap` prw = ProductConst $ unPC wf <*> unPC prw++ -- | Get width 'Int' value of record type 'a'. runPersistableRecordWidth :: PersistableRecordWidth a -> Int-runPersistableRecordWidth (PersistableRecordWidth w) = w+runPersistableRecordWidth = getSum . getConst . unPC+{-# INLINE runPersistableRecordWidth #-} +instance Show a => Show (ProductConst a b) where+  show = ("PC " ++) . show . getConst . unPC+ -- | Unsafely generate 'PersistableRecordWidth' proof object from specified width of Haskell type 'a'. unsafePersistableRecordWidth :: Int                      -- ^ Specify width of Haskell type 'a'                              -> PersistableRecordWidth a -- ^ Result proof object-unsafePersistableRecordWidth =  PersistableRecordWidth+unsafePersistableRecordWidth = ProductConst . Const . Sum+{-# INLINE unsafePersistableRecordWidth #-}  -- | Unsafely generate 'PersistableRecordWidth' proof object for Haskell type 'a' which is single column type. unsafeValueWidth :: PersistableRecordWidth a unsafeValueWidth =  unsafePersistableRecordWidth 1+{-# INLINE unsafeValueWidth #-}  -- | Derivation rule of 'PersistableRecordWidth' for tuple (,) type. (<&>) :: PersistableRecordWidth a -> PersistableRecordWidth b -> PersistableRecordWidth (a, b)-a <&> b = PersistableRecordWidth $ runPersistableRecordWidth a + runPersistableRecordWidth b+a <&> b = (,) `pmap` a `pap` b  -- | Derivation rule of 'PersistableRecordWidth' from from Haskell type 'a' into for Haskell type 'Maybe' 'a'. maybeWidth :: PersistableRecordWidth a -> PersistableRecordWidth (Maybe a)-maybeWidth =  PersistableRecordWidth . runPersistableRecordWidth---- | Axiom of 'PersistableRecordWidth' for Haskell unit () type.-voidWidth :: PersistableRecordWidth ()-voidWidth =  unsafePersistableRecordWidth 0+maybeWidth = pmap Just   -- | Interface of inference rule for 'PersistableSqlType' proof object@@ -84,17 +117,45 @@ class PersistableWidth a where   persistableWidth :: PersistableRecordWidth a --- | Inference rule of 'PersistableRecordWidth' proof object for tuple ('a', 'b') type.-instance (PersistableWidth a, PersistableWidth b) => PersistableWidth (a, b) where-  persistableWidth = persistableWidth <&> persistableWidth+  default persistableWidth :: (Generic a, GFieldWidthList (Rep a)) => PersistableRecordWidth a+  persistableWidth = pmapConst (Sum . lastA) genericFieldOffsets+    where+      lastA a = a ! (snd $ bounds a) ++pmapConst :: (a -> b) -> ProductConst a c -> ProductConst b c+pmapConst f = ProductConst . Const . f . getConst . unPC++-- | Generic width value list of record fields.+class GFieldWidthList f where+  gFieldWidthList :: ProductConst (DList Int) (f a)++instance GFieldWidthList U1 where+  gFieldWidthList = ProductConst $ pure U1++instance (GFieldWidthList a, GFieldWidthList b) => GFieldWidthList (a :*: b) where+  gFieldWidthList = (:*:) `pmap` gFieldWidthList `pap` gFieldWidthList++instance GFieldWidthList a => GFieldWidthList (M1 i c a) where+  gFieldWidthList = M1 `pmap` gFieldWidthList++instance PersistableWidth a => GFieldWidthList (K1 i a) where+  gFieldWidthList = K1 `pmap` pmapConst (pure . getSum) persistableWidth++offsets :: [Int] -> Array Int Int+offsets ws = listArray (0, length ws) $ scanl (+) 0 ws++-- | Generic offset array of record fields.+genericFieldOffsets :: (Generic a, GFieldWidthList (Rep a)) => ProductConst (Array Int Int) a+genericFieldOffsets = pmapConst (offsets . DList.toList) $ to `pmap` gFieldWidthList++ -- | Inference rule of 'PersistableRecordWidth' proof object for 'Maybe' type. instance PersistableWidth a => PersistableWidth (Maybe a) where   persistableWidth = maybeWidth persistableWidth  -- | Inference rule of 'PersistableRecordWidth' for Haskell unit () type. Derive from axiom.-instance PersistableWidth () where-  persistableWidth = voidWidth+instance PersistableWidth ()  -- default generic instance  -- | Pass type parameter and inferred width value. derivedWidth :: PersistableWidth a => (PersistableRecordWidth a, Int)
src/Database/Record/TH.hs view
@@ -4,7 +4,7 @@  -- | -- Module      : Database.Record.TH--- Copyright   : 2013 Kei Hibino+-- Copyright   : 2013-2017 Kei Hibino -- License     : BSD3 -- -- Maintainer  : ex8k.hibino@gmail.com@@ -18,9 +18,6 @@   defineRecord,   defineRecordWithConfig, -  -- * Deriving class symbols-  derivingEq, derivingShow, derivingRead, derivingData, derivingTypeable,-   -- * Table constraint specified by key   defineHasColumnConstraintInstance,   defineHasPrimaryConstraintInstanceDerived,@@ -31,70 +28,60 @@   defineRecordType,   defineRecordTypeWithConfig, -  -- * Function declarations depending on SQL type-  makeRecordPersistableWithSqlType,-  makeRecordPersistableWithSqlTypeWithConfig,-  makeRecordPersistableWithSqlTypeDefault,-   -- * Function declarations against defined record types-  makeRecordPersistableWithSqlTypeFromDefined,-  makeRecordPersistableWithSqlTypeDefaultFromDefined,   defineColumnOffsets,    recordWidthTemplate, -  defineRecordParser,-  defineRecordPrinter,--  definePersistableInstance,-   -- * Reify   reifyRecordType, -  -- * Templates about record type name+  -- * Templates about record name   NameConfig,  defaultNameConfig,   recordTypeName, columnName, -  recordType,+  recordTemplate,    columnOffsetsVarNameDefault, -  persistableFunctionNamesDefault,-   -- * Not nullable single column type-  deriveNotNullType-  ) where+  deriveNotNullType, +  -- * Template for tuple types+  defineTupleInstances,+  ) where -import Control.Applicative (pure, (<*>))-import Data.List (foldl')-import Data.Array (Array, listArray, (!))-import Data.Data (Data, Typeable)+import GHC.Generics (Generic)+import Data.Array (Array) import Language.Haskell.TH.Name.CamelCase   (ConName(conName), VarName(varName),    conCamelcaseName, varCamelcaseName, varNameWithPrefix,-   toTypeCon, toDataCon, toVarExp)-import Language.Haskell.TH.Lib.Extra (integralE, simpleValD)+   toTypeCon, toDataCon, )+import Language.Haskell.TH.Lib.Extra (integralE, simpleValD, reportWarning) import Language.Haskell.TH.Compat.Data (dataD', unDataD) import Language.Haskell.TH-  (Q, newName, nameBase, reify, Info(TyConI), Name,+  (Q, nameBase, reify, Info(TyConI), Name,    TypeQ, conT, Con (NormalC, RecC),-   Dec, sigD, valD,-   ExpQ, Exp(ConE), conE, varE, lamE, listE, sigE,-   varP, conP, normalB, recC,+   Dec,+   ExpQ, conE, listE, sigE,+   recC,    cxt, varStrictType, strictType, isStrict) +import Control.Arrow ((&&&))+ import Database.Record   (HasColumnConstraint(columnConstraint), Primary, NotNull,    HasKeyConstraint(keyConstraint), derivedCompositePrimary,    PersistableRecordWidth, PersistableWidth(persistableWidth),-   FromSql(recordFromSql), RecordFromSql,-   ToSql(recordToSql), RecordToSql, wrapToSql, putRecord, putEmpty)+   FromSql, ToSql, )  import Database.Record.KeyConstraint   (unsafeSpecifyColumnConstraint, unsafeSpecifyNotNullValue, unsafeSpecifyKeyConstraint)-import Database.Record.Persistable (unsafePersistableRecordWidth, runPersistableRecordWidth)+import Database.Record.Persistable+  (runPersistableRecordWidth,+   ProductConst, getProductConst, genericFieldOffsets) import qualified Database.Record.Persistable as Persistable+import Database.Record.InternalTH (defineTupleInstances)   -- | 'NameConfig' type to customize names of expanded record templates.@@ -119,12 +106,12 @@   , columnName      =  const varCamelcaseName   } --- | Record type constructor template from SQL table name 'String'.-recordType :: NameConfig -- ^ name rule config-           -> String     -- ^ Schema name string in SQL-           -> String     -- ^ Table name string in SQL-           -> TypeQ      -- ^ Record type constructor-recordType config scm = toTypeCon . recordTypeName config scm+-- | Record constructor templates from SQL table name 'String'.+recordTemplate :: NameConfig    -- ^ name rule config+               -> String        -- ^ Schema name string in SQL+               -> String        -- ^ Table name string in SQL+               -> (TypeQ, ExpQ) -- ^ Record type and data constructor+recordTemplate config scm = (toTypeCon &&& toDataCon) . recordTypeName config scm  -- | Variable expression of record column offset array. columnOffsetsVarNameDefault :: Name    -- ^ Table type name@@ -170,31 +157,6 @@ defineHasNotNullKeyInstance =   defineHasColumnConstraintInstance [t| NotNull |] -{-# DEPRECATED derivingEq "Use TH quasi-quotation like ''Eq instead of this." #-}--- | Name to specify deriving 'Eq'-derivingEq   :: Name-derivingEq   = ''Eq--{-# DEPRECATED derivingShow "Use TH quasi-quotation like ''Show instead of this." #-}--- | Name to specify deriving 'Show'-derivingShow :: Name-derivingShow = ''Show--{-# DEPRECATED derivingRead "Use TH quasi-quotation like ''Read instead of this." #-}--- | Name to specify deriving 'Read'-derivingRead :: Name-derivingRead = ''Read--{-# DEPRECATED derivingData "Use TH quasi-quotation like ''Data instead of this." #-}--- | Name to specify deriving 'Data'-derivingData :: Name-derivingData = ''Data--{-# DEPRECATED derivingTypeable "Use TH quasi-quotation like ''Typeable instead of this." #-}--- | Name to specify deriving 'Typeable'-derivingTypeable :: Name-derivingTypeable = ''Typeable- -- | Record type width expression template. recordWidthTemplate :: TypeQ -- ^ Record type constructor.                     -> ExpQ  -- ^ Expression to get record width.@@ -211,10 +173,8 @@   let ofsVar = columnOffsetsVarNameDefault $ conName typeName'       widthIxE = integralE $ length tys   ar <- simpleValD (varName ofsVar) [t| Array Int Int |]-        [| listArray (0 :: Int, $widthIxE) $-           scanl (+) (0 :: Int) $(listE $ map recordWidthTemplate tys) |]-  pw <- [d| instance PersistableWidth $(toTypeCon typeName') where-              persistableWidth = unsafePersistableRecordWidth $ $(toVarExp ofsVar) ! $widthIxE+        [| getProductConst (genericFieldOffsets :: ProductConst (Array Int Int) $(toTypeCon typeName')) |]+  pw <- [d| instance PersistableWidth $(toTypeCon typeName')           |]   return $ ar ++ pw @@ -226,9 +186,14 @@ defineRecordType typeName' columns derives = do   let typeName = conName typeName'       fld (n, tq) = varStrictType (varName n) (strictType isStrict tq)-  rec  <- dataD' (cxt []) typeName [] [recC typeName (map fld columns)] derives+  derives1 <- if (''Generic `notElem` derives)+              then do reportWarning "HRR needs Generic instance, please add ''Generic manually."+                      return $ ''Generic : derives+                      {- DROP this hack in future version ups. -}+              else    return   derives+  rec' <- dataD' (cxt []) typeName [] [recC typeName (map fld columns)] derives1   offs <- defineColumnOffsets typeName' [ty | (_, ty) <- columns]-  return $ rec : offs+  return $ rec' : offs  -- | Record type declaration template with configured names. defineRecordTypeWithConfig :: NameConfig -> String -> String -> [(String, TypeQ)] -> [Name] -> Q [Dec]@@ -238,79 +203,6 @@   [ (columnName config schema n, t) | (n, t) <- columns ]  --- | Record parser template.-defineRecordParser :: TypeQ         -- ^ SQL value type.-                   -> VarName       -- ^ Name of record parser.-                   -> (TypeQ, ExpQ) -- ^ Record type constructor and data constructor.-                   -> Int           -- ^ Count of record columns.-                   -> Q [Dec]       -- ^ Declaration of record construct function from SQL values.-defineRecordParser sqlValType name' (tyCon, dataCon) width = do-  let name = varName name'-  sig <- sigD name [t| RecordFromSql $sqlValType $tyCon |]-  var <- valD (varP name)-         (normalB-          (foldl' (\a x -> [| $a <*> $x |]) [| pure $dataCon |]-           $ replicate width [| recordFromSql |])-         )-         []-  return [sig, var]--dataConInfo :: Exp -> Q Name-dataConInfo =  d  where-  d (ConE n) = return n-  d e        = fail $ "Not record data constructor: " ++ show e---- | Record printer template.-defineRecordPrinter :: TypeQ         -- ^ SQL value type.-                    -> VarName       -- ^ Name of record printer.-                    -> (TypeQ, ExpQ) -- ^ Record type constructor and data constructor.-                    -> Int           -- ^ Count of record columns.-                    -> Q [Dec]       -- ^ Declaration of record construct function from SQL values.-defineRecordPrinter sqlValType name' (tyCon, dataCon) width = do-  let name = varName name'-  sig <- sigD name [t| RecordToSql $sqlValType $tyCon |]-  names <- mapM (newName . ('f':) . show) [1 .. width]-  dcn <- dataCon >>= dataConInfo-  var <- valD (varP name)-         (normalB [| wrapToSql-                     $(lamE-                       [ conP dcn [ varP n | n <- names ] ]-                       (foldr (\a x -> [| $a >> $x |]) [| putEmpty () |]-                        [ [| putRecord $(varE n) |] | n <- names ])) |])-         []-  return [sig, var]---- | Record parser and printer instance templates for converting---   between list of SQL type and Haskell record type.-definePersistableInstance :: TypeQ   -- ^ SQL value type.-                           -> TypeQ   -- ^ Record type constructor.-                           -> VarName -- ^ Record parser name.-                           -> VarName -- ^ Record printer name.-                           -> Int     -- ^ Count of record columns.-                           -> Q [Dec] -- ^ Instance declarations for 'Persistable'.-definePersistableInstance sqlType typeCon parserName printerName _width = do-  [d| instance FromSql $sqlType $typeCon where-        recordFromSql = $(toVarExp parserName)--      instance ToSql $sqlType $typeCon where-        recordToSql = $(toVarExp printerName)-    |]---- | All templates depending on SQL value type.-makeRecordPersistableWithSqlType :: TypeQ              -- ^ SQL value type.-                                 -> (VarName, VarName) -- ^ Constructor function name and decompose function name.-                                 -> (TypeQ, ExpQ)      -- ^ Record type constructor and data constructor.-                                 -> Int                -- ^ Count of record columns.-                                 -> Q [Dec]            -- ^ Result declarations.-makeRecordPersistableWithSqlType-  sqlValueType-  (cF, dF) conPair@(tyCon, _)-  width = do-  fromSQL  <- defineRecordParser sqlValueType cF conPair width-  toSQL    <- defineRecordPrinter sqlValueType dF conPair width-  instSQL  <- definePersistableInstance sqlValueType tyCon cF dF width-  return $ fromSQL ++ toSQL ++ instSQL- -- | Default name of record construction function from SQL table name. fromSqlNameDefault :: String -> VarName fromSqlNameDefault =  (`varNameWithPrefix` "fromSqlOf")@@ -319,29 +211,6 @@ toSqlNameDefault :: String -> VarName toSqlNameDefault =  (`varNameWithPrefix` "toSqlOf") --- | All templates depending on SQL value type with configured names.-makeRecordPersistableWithSqlTypeWithConfig :: TypeQ      -- ^ SQL value type-                                         -> NameConfig -- ^ name rule config-                                         -> String     -- ^ Schema name of database-                                         -> String     -- ^ Table name of database-                                         -> Int        -- ^ Count of record columns-                                         -> Q [Dec]    -- ^ Result declarations-makeRecordPersistableWithSqlTypeWithConfig sqlValueType config schema table width =-  makeRecordPersistableWithSqlType-    sqlValueType-    (persistableFunctionNamesDefault . conName . conCamelcaseName $ table)-    (recordType config schema table, toDataCon . recordTypeName config schema $ table)-    width---- | All templates depending on SQL value type with default names.-makeRecordPersistableWithSqlTypeDefault :: TypeQ   -- ^ SQL value type-                                        -> String  -- ^ Schema name-                                        -> String  -- ^ Table name-                                        -> Int     -- ^ Count of record columns-                                        -> Q [Dec] -- ^ Result declarations-makeRecordPersistableWithSqlTypeDefault sqlValueType =-  makeRecordPersistableWithSqlTypeWithConfig sqlValueType defaultNameConfig- recordInfo' :: Info -> Maybe ((TypeQ, ExpQ), (Maybe [Name], [TypeQ])) recordInfo' =  d  where   d (TyConI tcon) = do@@ -362,41 +231,29 @@     return     (recordInfo' tyConInfo) --- | Generate persistable function symbol names using default rule.-persistableFunctionNamesDefault :: Name -> (VarName, VarName)-persistableFunctionNamesDefault recTypeName = (fromSqlNameDefault bn, toSqlNameDefault bn)  where-  bn = nameBase recTypeName---- | All templates depending on SQL value type. Defined record type information is used.-makeRecordPersistableWithSqlTypeFromDefined :: TypeQ              -- ^ SQL value type-                                            -> (VarName, VarName) -- ^ Constructor function name and decompose function name-                                            -> Name               -- ^ Record type constructor name-                                            -> Q [Dec]            -- ^ Result declarations-makeRecordPersistableWithSqlTypeFromDefined sqlValueType fnames recTypeName = do-  (conPair, (_, cts)) <- reifyRecordType recTypeName-  makeRecordPersistableWithSqlType sqlValueType fnames conPair $ length cts---- | All templates depending on SQL value type with default names. Defined record type information is used.-makeRecordPersistableWithSqlTypeDefaultFromDefined :: TypeQ   -- ^ SQL value type-                                                   -> Name    -- ^ Record type constructor name-                                                   -> Q [Dec] -- ^ Result declarations-makeRecordPersistableWithSqlTypeDefaultFromDefined sqlValueType recTypeName =-  makeRecordPersistableWithSqlTypeFromDefined sqlValueType (persistableFunctionNamesDefault recTypeName) recTypeName+-- | Record parser and printer instance templates for converting+--   between list of SQL type and Haskell record type.+definePersistableInstance :: TypeQ   -- ^ SQL value type.+                          -> TypeQ   -- ^ Record type constructor.+                          -> Q [Dec] -- ^ Instance declarations.+definePersistableInstance sqlType typeCon = do+  [d| instance FromSql $sqlType $typeCon+      instance ToSql $sqlType $typeCon+    |]  -- | All templates for record type. defineRecord :: TypeQ              -- ^ SQL value type-             -> (VarName, VarName) -- ^ Constructor function name and decompose function name              -> ConName            -- ^ Record type name              -> [(VarName, TypeQ)] -- ^ Column schema              -> [Name]             -- ^ Record derivings              -> Q [Dec]            -- ^ Result declarations defineRecord   sqlValueType-  fnames tyC+  tyC   columns drvs = do    typ     <- defineRecordType tyC columns drvs-  withSql <- makeRecordPersistableWithSqlType sqlValueType fnames (toTypeCon tyC, toDataCon tyC) $ length columns+  withSql <- definePersistableInstance sqlValueType $ toTypeCon tyC   return $ typ ++ withSql  -- | All templates for record type with configured names.@@ -409,7 +266,8 @@                      -> Q [Dec]           -- ^ Result declarations defineRecordWithConfig sqlValueType config schema table columns derives = do   typ     <- defineRecordTypeWithConfig config schema table columns derives-  withSql <- makeRecordPersistableWithSqlTypeWithConfig sqlValueType config schema table $ length columns+  withSql <- definePersistableInstance sqlValueType . fst $ recordTemplate config schema table+   return $ typ ++ withSql  
src/Database/Record/ToSql.hs view
@@ -1,10 +1,12 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE DefaultSignatures #-}  -- | -- Module      : Database.Record.ToSql--- Copyright   : 2013 Kei Hibino+-- Copyright   : 2013-2017 Kei Hibino -- License     : BSD3 -- -- Maintainer  : ex8k.hibino@gmail.com@@ -35,6 +37,7 @@   unsafeUpdateValuesWithIndexes   ) where +import GHC.Generics (Generic, Rep, U1 (..), K1 (..), M1 (..), (:*:)(..), from) import Data.Array (listArray, (!)) import Data.Set (toList, fromList, (\\)) import Control.Monad.Trans.Writer (Writer, execWriter, tell)@@ -75,12 +78,21 @@                   -> RecordToSql q a -- ^ Result proof object createRecordToSql f =  wrapToSql $ tell . DList.fromList . f --- | Derivation rule of 'RecordToSql' proof object for Haskell tuple (,) type.-(<&>) :: RecordToSql q a -> RecordToSql q b -> RecordToSql q (a, b)-ra <&> rb = RecordToSql $ \(a, b) -> do+-- unsafely map record+mapToSql :: (a -> b) -> RecordToSql q b -> RecordToSql q a+mapToSql f x = wrapToSql $ runRecordToSql x . f++-- unsafely put product record+productToSql :: (c -> (a -> b -> ToSqlM q ()) -> ToSqlM q ())+             -> RecordToSql q a -> RecordToSql q b -> RecordToSql q c+productToSql run ra rb = wrapToSql $ \c -> run c $ \a b -> do   runRecordToSql ra a   runRecordToSql rb b +-- | Derivation rule of 'RecordToSql' proof object for Haskell tuple (,) type.+(<&>) :: RecordToSql q a -> RecordToSql q b -> RecordToSql q (a, b)+(<&>) = productToSql $ flip uncurry+ -- | Derivation rule of 'RecordToSql' proof object for Haskell 'Maybe' type. maybeRecord :: PersistableSqlType q -> PersistableRecordWidth a -> RecordToSql q a -> RecordToSql q (Maybe a) maybeRecord qt w ra =  wrapToSql d  where@@ -91,24 +103,37 @@   -- | Inference rule interface for 'RecordToSql' proof object.-class PersistableWidth a => ToSql q a where+class ToSql q a where   -- | Infer 'RecordToSql' proof object.   recordToSql :: RecordToSql q a --- | Inference rule of 'RecordToSql' proof object which can convert---   from Haskell tuple ('a', 'b') type into list of SQL type ['q'].-instance (ToSql q a, ToSql q b) => ToSql q (a, b) where-  recordToSql = recordToSql <&> recordToSql+  default recordToSql :: (Generic a, GToSql q (Rep a)) => RecordToSql q a+  recordToSql = from `mapToSql` gToSql +class GToSql q f where+  gToSql :: RecordToSql q (f a)++instance GToSql q U1 where+  gToSql = wrapToSql $ \U1 -> tell DList.empty++instance (GToSql q a, GToSql q b) => GToSql q (a :*: b) where+  gToSql = productToSql (\ (a:*:b) f -> f a b) gToSql gToSql++instance GToSql q a => GToSql q (M1 i c a) where+  gToSql = (\(M1 a) -> a) `mapToSql` gToSql++instance ToSql q a => GToSql q (K1 i a) where+  gToSql = (\(K1 a) -> a) `mapToSql` recordToSql++ -- | Inference rule of 'RecordToSql' proof object which can convert --   from Haskell 'Maybe' type into list of SQL type ['q'].-instance (PersistableType q, ToSql q a) => ToSql q (Maybe a)  where+instance (PersistableType q, PersistableWidth a, ToSql q a) => ToSql q (Maybe a)  where   recordToSql = maybeRecord persistableType persistableWidth recordToSql  -- | Inference rule of 'RecordToSql' proof object which can convert --   from Haskell unit () type into /empty/ list of SQL type ['q'].-instance ToSql q () where-  recordToSql = wrapToSql $ \() -> tell DList.empty+instance ToSql q ()  -- default generic instance  -- | Run inferred 'RecordToSql' proof object. --   Context to convert haskell record type 'a' into SQL type 'q' list.
+ src/Database/Record/TupleInstances.hs view
@@ -0,0 +1,13 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}++module Database.Record.TupleInstances () where++import Control.Applicative ((<$>))++import Database.Record.InternalTH (defineTupleInstances)+++$(concat <$> mapM defineTupleInstances [2..7])+-- Generic instances of tuple types are generated from 2 to 7 in GHC.Generics.
+ test/Model.hs view
@@ -0,0 +1,74 @@+{-# OPTIONS -fno-warn-orphans #-}+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}+{-# LANGUAGE DeriveGeneric #-}+module Model where++import GHC.Generics (Generic)++import Database.Record+  (PersistableType (..), PersistableWidth (..),+   FromSql (..), valueRecordFromSql,+   ToSql (..), valueRecordToSql)+import Database.Record.KeyConstraint (HasColumnConstraint (..), NotNull, unsafeSpecifyColumnConstraint)+import Database.Record.Persistable (unsafePersistableSqlTypeFromNull, unsafeValueWidth, )+++instance PersistableType String where+  persistableType = unsafePersistableSqlTypeFromNull "<null>"+++instance PersistableWidth String where+  persistableWidth = unsafeValueWidth++instance PersistableWidth Int where+  persistableWidth = unsafeValueWidth++instance FromSql String String where+  recordFromSql = valueRecordFromSql id++instance FromSql String Int where+  recordFromSql = valueRecordFromSql read++instance ToSql String String where+  recordToSql = valueRecordToSql id++instance ToSql String Int where+  recordToSql = valueRecordToSql show+++data User =+  User+  { uid    ::  Int+  , uname  ::  String+  , note   ::  String+  } deriving (Eq, Show, Generic)++data Group =+  Group+  { gid    ::  Int+  , gname  ::  String+  } deriving (Eq, Show, Generic)++data Membership =+  Membership+  { user   ::  User+  , group  ::  Maybe Group+  } deriving (Eq, Show, Generic)++instance HasColumnConstraint NotNull User where+  columnConstraint = unsafeSpecifyColumnConstraint 0++instance HasColumnConstraint NotNull Group where+  columnConstraint = unsafeSpecifyColumnConstraint 0++instance PersistableWidth User+instance PersistableWidth Group+instance PersistableWidth Membership++instance FromSql String User+instance FromSql String Group+instance FromSql String Membership++instance ToSql String User+instance ToSql String Group+instance ToSql String Membership
test/nestedEq.hs view
@@ -1,74 +1,60 @@ {-# OPTIONS -fno-warn-orphans #-} {-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-} -import Control.Applicative ((<$>), (<*>)) import Test.QuickCheck.Simple (defaultMain, eqTest) -import Database.Record-  (PersistableType (..), PersistableWidth (..),-   FromSql (..), valueRecordFromSql, toRecord,-   ToSql (..), valueRecordToSql)-import Database.Record.Persistable-  (unsafePersistableSqlTypeFromNull, unsafeValueWidth)---instance PersistableType String where-  persistableType = unsafePersistableSqlTypeFromNull "<null>"---instance FromSql String String where-  recordFromSql = valueRecordFromSql id--instance FromSql String Int where-  recordFromSql = valueRecordFromSql read--instance PersistableWidth String where-  persistableWidth = unsafeValueWidth--instance ToSql String String where-  recordToSql = valueRecordToSql id--instance PersistableWidth Int where-  persistableWidth = unsafeValueWidth+import Database.Record (toRecord, fromRecord, persistableWidth, PersistableRecordWidth)+import Database.Record.Persistable (runPersistableRecordWidth) -instance ToSql String Int where-  recordToSql = valueRecordToSql show+import Model (User (..), Group (..), Membership (..))  -data User =-  User-  { uid    ::  Int-  , uname  ::  String-  , note   ::  String-  } deriving (Eq, Show)--data Group =-  Group-  { gid    ::  Int-  , gname  ::  String-  } deriving (Eq, Show)--data Membership =-  Membership-  { user   ::  User-  , group  ::  Group-  } deriving (Eq, Show)--instance FromSql String User where-  recordFromSql = User <$> recordFromSql <*> recordFromSql <*> recordFromSql--instance FromSql String Group where-  recordFromSql = Group <$> recordFromSql <*> recordFromSql--instance FromSql String Membership where-  recordFromSql = Membership <$> recordFromSql <*> recordFromSql- main :: IO () main =   defaultMain   [ eqTest-    "nestedEq"+    "toRecord just"     (Membership { user  = User { uid = 1, uname = "Kei Hibino", note = "HRR developer" }-                , group = Group { gid = 1, gname = "Haskellers" }+                , group = Just $ Group { gid = 1, gname = "Haskellers" }                 } )-    (toRecord ["1", "Kei Hibino", "HRR developer", "1", "Haskellers"]) ]+    (toRecord ["1", "Kei Hibino", "HRR developer", "1", "Haskellers"])+  , eqTest+    "toRecord nothing"+    (Membership { user  = User { uid = 1, uname = "Kei Hibino", note = "HRR developer" }+                , group = Nothing+                } )+    (toRecord ["1", "Kei Hibino", "HRR developer", "<null>", "<null>"])+  , eqTest+    "fromRecord just"+    (fromRecord $ Membership { user  = User { uid = 1, uname = "Kei Hibino", note = "HRR developer" }+                             , group = Just $ Group { gid = 1, gname = "Haskellers" }+                             } )+    ["1", "Kei Hibino", "HRR developer", "1", "Haskellers"]+  , eqTest+    "fromRecord note"+    (fromRecord $ Membership { user  = User { uid = 1, uname = "Kei Hibino", note = "HRR developer" }+                             , group = Nothing+                             } )+    ["1", "Kei Hibino", "HRR developer", "<null>", "<null>"]++  , eqTest+    "toRecord pair"+    (User { uid = 1, uname = "Kei Hibino", note = "HRR developer" },+     Just $ Group { gid = 1, gname = "Haskellers" })+    (toRecord ["1", "Kei Hibino", "HRR developer", "1", "Haskellers"])+  , eqTest+    "fromRecord pair"+    (fromRecord $ (User { uid = 1, uname = "Kei Hibino", note = "HRR developer" },+                   Just $ Group { gid = 1, gname = "Haskellers" }))+    ["1", "Kei Hibino", "HRR developer", "1", "Haskellers"]+  , eqTest+    "width pair"+    (runPersistableRecordWidth (persistableWidth :: PersistableRecordWidth User) ++     runPersistableRecordWidth (persistableWidth :: PersistableRecordWidth Group))+    (runPersistableRecordWidth (persistableWidth :: PersistableRecordWidth (User, Group)))+  , eqTest+    "width record"+    (runPersistableRecordWidth (persistableWidth :: PersistableRecordWidth User) ++     runPersistableRecordWidth (persistableWidth :: PersistableRecordWidth (Maybe Group)))+    (runPersistableRecordWidth (persistableWidth :: PersistableRecordWidth Membership))+  ]