persistent-redis 0.3.0 → 0.3.1
raw patch · 8 files changed
+276/−171 lines, 8 files
Files
- Database/Persist/Redis.hs +2/−0
- Database/Persist/Redis/Exception.hs +21/−0
- Database/Persist/Redis/Internal.hs +6/−154
- Database/Persist/Redis/Parser.hs +162/−0
- Database/Persist/Redis/Store.hs +11/−12
- Database/Persist/Redis/Update.hs +66/−0
- persistent-redis.cabal +4/−1
- tests/basic-test.hs +4/−4
Database/Persist/Redis.hs view
@@ -1,7 +1,9 @@ module Database.Persist.Redis ( module Database.Persist.Redis.Config , module Database.Persist.Redis.Store+ , module Database.Persist.Redis.Exception ) where import Database.Persist.Redis.Config import Database.Persist.Redis.Store+import Database.Persist.Redis.Exception
+ Database/Persist/Redis/Exception.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE DeriveDataTypeable #-}+module Database.Persist.Redis.Exception+ ( RedisException (..)+ ) where++import Data.Typeable (Typeable)+import Control.Exception (Exception)++data RedisException = NotSupportedOperation String+ | ParserError String+ | NotSupportedValueType String+ | IncorrectUpdate String+ | IncorrectBehavior+ deriving Typeable+instance Show RedisException where+ show (NotSupportedOperation key) = "The operation is not supported: " ++ key+ show (ParserError msg) = "Error during parsing: " ++ msg+ show (NotSupportedValueType tp) = "The value type " ++ tp ++ " is not supported for Redis"+ show IncorrectBehavior = "The behavior of persistent-redis is incorrect"+ show (IncorrectUpdate msg) = "This update is not possible: " ++ msg+instance Exception RedisException
Database/Persist/Redis/Internal.hs view
@@ -1,31 +1,23 @@ {-# LANGUAGE OverloadedStrings #-} module Database.Persist.Redis.Internal- ( toInsertFields+ ( toKey+ , unKey+ , mkEntity , toKeyId- , toEntityName , toKeyText+ , toInsertFields , toB- , mkEntity- , unKey- , toKey ) where -import Control.Arrow((***))-import Data.Fixed-import Data.Time-import Data.Int (Int64)-import Data.Word (Word8)-import Control.Monad (liftM, liftM3)-import Data.Binary (Binary(..), encode, getWord8, Get)-import qualified Data.Binary as Q import Data.Text (Text, unpack) import qualified Data.Text as T import Database.Persist.Types import Database.Persist.Class import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.UTF8 as U +import Database.Persist.Redis.Parser+ toLabel :: FieldDef -> B.ByteString toLabel = U.fromString . unpack . unDBName . fieldDB @@ -34,146 +26,6 @@ toEntityName :: EntityDef -> B.ByteString toEntityName = U.fromString . unpack . unDBName . entityDB--newtype BinText = BinText { unBinText :: Text }-instance Binary BinText where- put = put . U.fromString . unpack . unBinText- get = do - str <- Q.get- return $ BinText $ (T.pack . U.toString) str--newtype BinPico= BinPico { unBinPico :: Pico } -instance Binary BinPico where- put = put . toRational . unBinPico- get = do- x <- Q.get :: Get Rational- return $ BinPico (fromRational x)--newtype BinDiffTime = BinDiffTime { unBinDiffTime :: DiffTime }-instance Binary BinDiffTime where- put = put . toRational . unBinDiffTime- get = do- x <- Q.get :: Get Rational- return $ BinDiffTime (fromRational x)--newtype BinDay = BinDay { unBinDay :: Day }-instance Binary BinDay where- put (BinDay (ModifiedJulianDay x)) = put x- get = do- x <- Q.get :: Get Integer- return $ BinDay (ModifiedJulianDay x)--newtype BinTimeOfDay = BinTimeOfDay { unBinTimeOfDay :: TimeOfDay }-instance Binary BinTimeOfDay where- put (BinTimeOfDay (TimeOfDay h m s)) = do- put h- put m- put (BinPico s)- get = do- let s = liftM unBinPico (Q.get :: Get BinPico)- let tod = liftM3 TimeOfDay (Q.get :: Get Int) (Q.get :: Get Int) s- liftM BinTimeOfDay tod--{- -newtype BinZT = BinZT { unBinZT :: ZT }-instance Binary BinZT where- put (BinZT (ZT (ZonedTime (LocalTime day timeOfDay) (TimeZone mins summer name)))) = do- put (BinDay day)- put (BinTimeOfDay timeOfDay)- put mins- put summer- put name-- get = do- day <- Q.get :: Get BinDay- timeOfDay <- Q.get :: Get BinTimeOfDay- mins <- Q.get :: Get Int- summer <- Q.get :: Get Bool- name <- Q.get :: Get String- return $ BinZT $ ZT (ZonedTime (LocalTime (unBinDay day) (unBinTimeOfDay timeOfDay)) (TimeZone mins summer name))--}-newtype BinPersistValue = BinPersistValue { unBinPersistValue :: PersistValue }-instance Binary BinPersistValue where- put (BinPersistValue (PersistText x)) = do- put (1 :: Word8)- put $ (U.fromString . unpack) x-- put (BinPersistValue (PersistByteString x)) = do- put (2 :: Word8)- put x-- put (BinPersistValue (PersistInt64 x)) = do- put (3 :: Word8)- put x-- put (BinPersistValue (PersistDouble x)) = do- put (4 :: Word8)- put x-- put (BinPersistValue (PersistBool x)) = do - put (5 :: Word8)- put x-- put (BinPersistValue (PersistDay day)) = do- put (6 :: Word8)- put (BinDay day)-- put (BinPersistValue (PersistTimeOfDay tod)) = do- put (7 :: Word8)- put (BinTimeOfDay tod)-- put (BinPersistValue (PersistUTCTime (UTCTime day pc))) = do- put (8 :: Word8)- put (BinDay day)- put (BinDiffTime pc)-- put (BinPersistValue PersistNull) = put (9 :: Word8)- put (BinPersistValue (PersistList x)) = do - put (10 :: Word8)- put (map BinPersistValue x)-- put (BinPersistValue (PersistMap x)) = do- put (11 :: Word8)- put (map (BinText *** BinPersistValue) x)-- put (BinPersistValue (PersistRational x)) = do- put (12 :: Word8)- put x-- put (BinPersistValue (PersistDbSpecific _)) = undefined- put (BinPersistValue (PersistObjectId _)) = error "PersistObjectId is not supported."-- get = do- tag <- getWord8- let pv = case tag of- 1 -> liftM (PersistText . unBinText) (Q.get :: Get BinText)- 2 -> liftM PersistByteString (Q.get :: Get B.ByteString)- 3 -> liftM PersistInt64 (Q.get :: Get Int64)- 4 -> liftM PersistDouble (Q.get :: Get Double)- 5 -> liftM PersistBool (Q.get :: Get Bool)- 6 -> liftM (PersistDay . unBinDay) (Q.get :: Get BinDay)- 7 -> liftM (PersistTimeOfDay . unBinTimeOfDay) (Q.get :: Get BinTimeOfDay)- 8 -> do- d <- Q.get :: Get BinDay- dt <- Q.get :: Get BinDiffTime- let utctime = UTCTime (unBinDay d) (unBinDiffTime dt)- return $ PersistUTCTime utctime- 9 -> return PersistNull- 10-> liftM (PersistList . map unBinPersistValue) (Q.get :: Get [BinPersistValue])- 11-> liftM (PersistMap . map (unBinText *** unBinPersistValue)) (Q.get :: Get [(BinText, BinPersistValue)])- 12-> liftM PersistRational (Q.get :: Get Rational)--- 13-> liftM (PersistZonedTime . unBinZT) (Q.get :: Get BinZT)- z -> fail ("Incorrect tag " ++ show z ++ " came to Binary deserialization")- liftM BinPersistValue pv--toValue :: PersistValue -> B.ByteString-toValue = L.toStrict . encode . BinPersistValue--castOne :: B.ByteString -> PersistValue-castOne = unBinPersistValue . Q.decode . L.fromStrict--redisToPerisistValues :: [(B.ByteString, B.ByteString)] -> [PersistValue]-redisToPerisistValues = map (castOne . snd) mkEntity :: (Monad m, PersistEntity val) => Key val -> [(B.ByteString, B.ByteString)] -> m (Entity val) mkEntity key fields = do
+ Database/Persist/Redis/Parser.hs view
@@ -0,0 +1,162 @@+module Database.Persist.Redis.Parser + ( redisToPerisistValues+ , toValue+ ) where++import Data.Fixed+import Data.Time+import Data.Int (Int64)+import Data.Word (Word8)+import Data.Text (Text, unpack)+import qualified Data.Text as T+import Data.Binary (Binary(..), encode, getWord8, Get)+import qualified Data.Binary as Q+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString.UTF8 as U+import Control.Arrow((***))+import Control.Monad (liftM, liftM3)+import Control.Exception (throw)++import Database.Persist.Types+import Database.Persist.Redis.Exception++newtype BinText = BinText { unBinText :: Text }+instance Binary BinText where+ put = put . U.fromString . unpack . unBinText+ get = do + str <- Q.get+ return $ BinText $ (T.pack . U.toString) str++newtype BinPico= BinPico { unBinPico :: Pico } +instance Binary BinPico where+ put = put . toRational . unBinPico+ get = do+ x <- Q.get :: Get Rational+ return $ BinPico (fromRational x)++newtype BinDiffTime = BinDiffTime { unBinDiffTime :: DiffTime }+instance Binary BinDiffTime where+ put = put . toRational . unBinDiffTime+ get = do+ x <- Q.get :: Get Rational+ return $ BinDiffTime (fromRational x)++newtype BinDay = BinDay { unBinDay :: Day }+instance Binary BinDay where+ put (BinDay (ModifiedJulianDay x)) = put x+ get = do+ x <- Q.get :: Get Integer+ return $ BinDay (ModifiedJulianDay x)++newtype BinTimeOfDay = BinTimeOfDay { unBinTimeOfDay :: TimeOfDay }+instance Binary BinTimeOfDay where+ put (BinTimeOfDay (TimeOfDay h m s)) = do+ put h+ put m+ put (BinPico s)+ get = do+ let s = liftM unBinPico (Q.get :: Get BinPico)+ let tod = liftM3 TimeOfDay (Q.get :: Get Int) (Q.get :: Get Int) s+ liftM BinTimeOfDay tod++{- +newtype BinZT = BinZT { unBinZT :: ZT }+instance Binary BinZT where+ put (BinZT (ZT (ZonedTime (LocalTime day timeOfDay) (TimeZone mins summer name)))) = do+ put (BinDay day)+ put (BinTimeOfDay timeOfDay)+ put mins+ put summer+ put name++ get = do+ day <- Q.get :: Get BinDay+ timeOfDay <- Q.get :: Get BinTimeOfDay+ mins <- Q.get :: Get Int+ summer <- Q.get :: Get Bool+ name <- Q.get :: Get String+ return $ BinZT $ ZT (ZonedTime (LocalTime (unBinDay day) (unBinTimeOfDay timeOfDay)) (TimeZone mins summer name))+-}+newtype BinPersistValue = BinPersistValue { unBinPersistValue :: PersistValue }+instance Binary BinPersistValue where+ put (BinPersistValue (PersistText x)) = do+ put (1 :: Word8)+ put $ (U.fromString . unpack) x++ put (BinPersistValue (PersistByteString x)) = do+ put (2 :: Word8)+ put x++ put (BinPersistValue (PersistInt64 x)) = do+ put (3 :: Word8)+ put x++ put (BinPersistValue (PersistDouble x)) = do+ put (4 :: Word8)+ put x++ put (BinPersistValue (PersistBool x)) = do + put (5 :: Word8)+ put x++ put (BinPersistValue (PersistDay day)) = do+ put (6 :: Word8)+ put (BinDay day)++ put (BinPersistValue (PersistTimeOfDay tod)) = do+ put (7 :: Word8)+ put (BinTimeOfDay tod)++ put (BinPersistValue (PersistUTCTime (UTCTime day pc))) = do+ put (8 :: Word8)+ put (BinDay day)+ put (BinDiffTime pc)++ put (BinPersistValue PersistNull) = put (9 :: Word8)+ put (BinPersistValue (PersistList x)) = do + put (10 :: Word8)+ put (map BinPersistValue x)++ put (BinPersistValue (PersistMap x)) = do+ put (11 :: Word8)+ put (map (BinText *** BinPersistValue) x)++ put (BinPersistValue (PersistRational x)) = do+ put (12 :: Word8)+ put x++ put (BinPersistValue (PersistDbSpecific _)) = throw $ NotSupportedValueType "PersistDbSpecific"+ put (BinPersistValue (PersistObjectId _)) = throw $ NotSupportedValueType "PersistObjectId"++ get = do+ tag <- getWord8+ let pv = case tag of+ 1 -> liftM (PersistText . unBinText) (Q.get :: Get BinText)+ 2 -> liftM PersistByteString (Q.get :: Get B.ByteString)+ 3 -> liftM PersistInt64 (Q.get :: Get Int64)+ 4 -> liftM PersistDouble (Q.get :: Get Double)+ 5 -> liftM PersistBool (Q.get :: Get Bool)+ 6 -> liftM (PersistDay . unBinDay) (Q.get :: Get BinDay)+ 7 -> liftM (PersistTimeOfDay . unBinTimeOfDay) (Q.get :: Get BinTimeOfDay)+ 8 -> do+ d <- Q.get :: Get BinDay+ dt <- Q.get :: Get BinDiffTime+ let utctime = UTCTime (unBinDay d) (unBinDiffTime dt)+ return $ PersistUTCTime utctime+ 9 -> return PersistNull+ 10-> liftM (PersistList . map unBinPersistValue) (Q.get :: Get [BinPersistValue])+ 11-> liftM (PersistMap . map (unBinText *** unBinPersistValue)) (Q.get :: Get [(BinText, BinPersistValue)])+ 12-> liftM PersistRational (Q.get :: Get Rational)+-- 13-> liftM (PersistZonedTime . unBinZT) (Q.get :: Get BinZT)+ z -> throw $ ParserError ("Incorrect tag " ++ show z ++ " came to Binary deserialization")+ liftM BinPersistValue pv++toValue :: PersistValue -> B.ByteString+toValue = L.toStrict . encode . BinPersistValue++castOne :: B.ByteString -> PersistValue+castOne = unBinPersistValue . Q.decode . L.fromStrict++redisToPerisistValues :: [(B.ByteString, B.ByteString)] -> [PersistValue]+redisToPerisistValues = map (castOne . snd)
Database/Persist/Redis/Store.hs view
@@ -13,10 +13,10 @@ import Control.Monad.IO.Class (MonadIO (..)) import qualified Database.Persist.Sql as Sql import qualified Database.Redis as R-import qualified Data.ByteString as B import Data.Text (Text, pack) import Database.Persist.Redis.Config (RedisT, thisConnection) import Database.Persist.Redis.Internal+import Database.Persist.Redis.Update import Web.PathPieces (PathPiece (..)) import Data.Aeson(FromJSON(..), ToJSON(..))@@ -86,17 +86,16 @@ Entity _ val <- mkEntity k r return $ Just val - update _ [] = return ()- update key upds = do- let fields = updatesToFields upds- _ <- execRedisT $ R.hmset (unKey key) fields- return ()--updatesToFields :: PersistEntity val => [Update val] -> [(B.ByteString, B.ByteString)]-updatesToFields = map updateToOneField- where- updateToOneField (Update field v up) = undefined- updateToOneField (BackendUpdate up) = undefined+ update _ [] = return ()+ update k upds = do+ r <- execRedisT $ R.hgetall (unKey k)+ if null r+ then fail "No such key exists!"+ else do+ v <- mkEntity k r+ let (Entity _ val) = cmdUpdate v upds+ insertKey k val+ return() instance PathPiece (BackendKey RedisBackend) where toPathPiece (RedisKey txt) = txt
+ Database/Persist/Redis/Update.hs view
@@ -0,0 +1,66 @@+{-# LANGUAGE RankNTypes #-}+module Database.Persist.Redis.Update+ ( cmdUpdate+ ) where++import Control.Exception (throw)+import Data.Functor.Identity+import Data.Functor.Constant+import Data.Either ()++import Database.Persist+import Database.Persist.Redis.Exception++type ASetter s t a b = (a -> Identity b) -> s -> Identity t++set :: ASetter s t a b -> b -> s -> t+set l b = runIdentity . l (\_ -> Identity b)++type Getting r s t a b = (a -> Constant r b) -> s -> Constant r t++view :: s -> Getting a s t a b -> a+view s l = getConstant (l Constant s)++cmdUpdate :: PersistEntity val => Entity val -> [Update val] -> Entity val+cmdUpdate = foldr updateOneField++updateOneField :: PersistEntity val => Update val -> Entity val -> Entity val+updateOneField (BackendUpdate _) _ = throw $ NotSupportedOperation "Backend specific update"+updateOneField (Update field v Assign) oldValue = set (fieldLens field) v oldValue+updateOneField (Update _ _ (BackendSpecificUpdate _)) _ = + throw $ NotSupportedOperation "Backend specific update withing update operation"++updateOneField (Update field v up) oldValue = set (fieldLens field) newValue oldValue+ where + lens = fieldLens field+ pv = toPersistValue v+ oldV = toPersistValue $ view oldValue lens+ eitherNewValue = fromPersistValue $ apply up oldV pv + newValue = either (\_ -> throw IncorrectBehavior) id eitherNewValue+++apply :: PersistUpdate -> PersistValue -> PersistValue -> PersistValue++apply Assign _ _ = throw IncorrectBehavior++apply Add (PersistInt64 x) (PersistInt64 y) = PersistInt64 (x + y)+apply Add (PersistDouble x) (PersistDouble y) = PersistDouble (x + y)+apply Add (PersistRational x) (PersistRational y) = PersistRational (x + y)+apply Add _ _ = throw $ IncorrectUpdate "Unable to apply addition to this field"++apply Subtract (PersistInt64 x) (PersistInt64 y) = PersistInt64 (x - y)+apply Subtract (PersistDouble x) (PersistDouble y) = PersistDouble (x - y)+apply Subtract (PersistRational x) (PersistRational y) = PersistRational (x - y)+apply Subtract _ _ = throw $ IncorrectUpdate "Unable to apply subtraction to this field"++apply Multiply (PersistInt64 x) (PersistInt64 y) = PersistInt64 (x * y)+apply Multiply (PersistDouble x) (PersistDouble y) = PersistDouble (x * y)+apply Multiply (PersistRational x) (PersistRational y) = PersistRational (x * y)+apply Multiply _ _ = throw $ IncorrectUpdate "Unable to apply subtraction to this field"++apply Divide (PersistInt64 x) (PersistInt64 y) = PersistInt64 (div x y)+apply Divide (PersistDouble x) (PersistDouble y) = PersistDouble (x / y)+apply Divide (PersistRational x) (PersistRational y) = PersistRational (x / y)+apply Divide _ _ = throw $ IncorrectUpdate "Unable to apply subtraction to this field"++apply (BackendSpecificUpdate _) _ _ = throw IncorrectBehavior
persistent-redis.cabal view
@@ -1,5 +1,5 @@ name: persistent-redis-version: 0.3.0+version: 0.3.1 license: BSD3 license-file: LICENSE author: Pavel Ryzhov <paul@paulrz.cz>@@ -38,6 +38,9 @@ other-modules: Database.Persist.Redis.Config Database.Persist.Redis.Internal Database.Persist.Redis.Store+ Database.Persist.Redis.Parser+ Database.Persist.Redis.Update+ Database.Persist.Redis.Exception ghc-options: -Wall
tests/basic-test.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE TemplateHaskell, QuasiQuotes, OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell, QuasiQuotes #-} {-# LANGUAGE TypeFamilies, EmptyDataDecls, GADTs #-} {-# LANGUAGE TypeSynonymInstances, MultiParamTypeClasses, GeneralizedNewtypeDeriving #-} module Main where@@ -36,10 +36,10 @@ main :: IO () main = withRedisConn redisConf $ runRedisPool $ do- liftIO $ print "Inserting..."+ _ <- liftIO $ print "Inserting..." s <- insert $ Person "Test" 12- liftIO $ ("Received the key" ++ (show s))- key <- mkKey "person_test"+ _ <- liftIO $ print ("Received the key" ++ show s)+ key <- mkKey (pack "person_test") insertKey key $ Person "Test2" 45 repsert s (Person "Test3" 55) g <- get key :: RedisT IO (Maybe Person)