packages feed

monarch 0.8.1.3 → 0.9.0.0

raw patch · 18 files changed

+1686/−1157 lines, 18 filesdep +containersdep +monarchdep +stmdep −HUnitdep ~bytestringdep ~criteriondep ~doctestPVP ok

version bump matches the API change (PVP)

Dependencies added: containers, monarch, stm

Dependencies removed: HUnit

Dependency ranges changed: bytestring, criterion, doctest, hspec, lifted-base, network, tokyotyrant-haskell, transformers

API changes (from Hackage documentation)

+ Database.Monarch: class MonadMonarch m
+ Database.Monarch.Mock: data MockDB
+ Database.Monarch.Mock: data MockT m a
+ Database.Monarch.Mock: newMockDB :: IO (TVar MockDB)
+ Database.Monarch.Mock: runMock :: MonadIO m => MockT m a -> TVar MockDB -> m (Either Code a)
- Database.Monarch: addDouble :: (MonadBaseControl IO m, MonadIO m) => ByteString -> Double -> MonarchT m Double
+ Database.Monarch: addDouble :: MonadMonarch m => ByteString -> Double -> m Double
- Database.Monarch: addInt :: (MonadBaseControl IO m, MonadIO m) => ByteString -> Int -> MonarchT m Int
+ Database.Monarch: addInt :: MonadMonarch m => ByteString -> Int -> m Int
- Database.Monarch: copy :: (MonadBaseControl IO m, MonadIO m) => ByteString -> MonarchT m ()
+ Database.Monarch: copy :: MonadMonarch m => ByteString -> m ()
- Database.Monarch: ext :: (MonadBaseControl IO m, MonadIO m) => ByteString -> [ExtOption] -> ByteString -> ByteString -> MonarchT m ByteString
+ Database.Monarch: ext :: MonadMonarch m => ByteString -> [ExtOption] -> ByteString -> ByteString -> m ByteString
- Database.Monarch: forwardMatchingKeys :: (MonadBaseControl IO m, MonadIO m) => ByteString -> Maybe Int -> MonarchT m [ByteString]
+ Database.Monarch: forwardMatchingKeys :: MonadMonarch m => ByteString -> Maybe Int -> m [ByteString]
- Database.Monarch: get :: (MonadBaseControl IO m, MonadIO m) => ByteString -> MonarchT m (Maybe ByteString)
+ Database.Monarch: get :: MonadMonarch m => ByteString -> m (Maybe ByteString)
- Database.Monarch: iterInit :: (MonadBaseControl IO m, MonadIO m) => MonarchT m ()
+ Database.Monarch: iterInit :: MonadMonarch m => m ()
- Database.Monarch: iterNext :: (MonadBaseControl IO m, MonadIO m) => MonarchT m (Maybe ByteString)
+ Database.Monarch: iterNext :: MonadMonarch m => m (Maybe ByteString)
- Database.Monarch: misc :: (MonadBaseControl IO m, MonadIO m) => ByteString -> [MiscOption] -> [ByteString] -> MonarchT m [ByteString]
+ Database.Monarch: misc :: MonadMonarch m => ByteString -> [MiscOption] -> [ByteString] -> m [ByteString]
- Database.Monarch: multipleGet :: (MonadBaseControl IO m, MonadIO m) => [ByteString] -> MonarchT m [(ByteString, ByteString)]
+ Database.Monarch: multipleGet :: MonadMonarch m => [ByteString] -> m [(ByteString, ByteString)]
- Database.Monarch: multipleOut :: (MonadBaseControl IO m, MonadIO m) => [ByteString] -> MonarchT m ()
+ Database.Monarch: multipleOut :: MonadMonarch m => [ByteString] -> m ()
- Database.Monarch: multiplePut :: (MonadBaseControl IO m, MonadIO m) => [(ByteString, ByteString)] -> MonarchT m ()
+ Database.Monarch: multiplePut :: MonadMonarch m => [(ByteString, ByteString)] -> m ()
- Database.Monarch: optimize :: (MonadBaseControl IO m, MonadIO m) => ByteString -> MonarchT m ()
+ Database.Monarch: optimize :: MonadMonarch m => ByteString -> m ()
- Database.Monarch: out :: (MonadBaseControl IO m, MonadIO m) => ByteString -> MonarchT m ()
+ Database.Monarch: out :: (MonadMonarch m, MonadBaseControl IO m, MonadIO m) => ByteString -> m ()
- Database.Monarch: put :: (MonadBaseControl IO m, MonadIO m) => ByteString -> ByteString -> MonarchT m ()
+ Database.Monarch: put :: MonadMonarch m => ByteString -> ByteString -> m ()
- Database.Monarch: putCat :: (MonadBaseControl IO m, MonadIO m) => ByteString -> ByteString -> MonarchT m ()
+ Database.Monarch: putCat :: MonadMonarch m => ByteString -> ByteString -> m ()
- Database.Monarch: putKeep :: (MonadBaseControl IO m, MonadIO m) => ByteString -> ByteString -> MonarchT m ()
+ Database.Monarch: putKeep :: MonadMonarch m => ByteString -> ByteString -> m ()
- Database.Monarch: putNoResponse :: (MonadBaseControl IO m, MonadIO m) => ByteString -> ByteString -> MonarchT m ()
+ Database.Monarch: putNoResponse :: MonadMonarch m => ByteString -> ByteString -> m ()
- Database.Monarch: putShiftLeft :: (MonadBaseControl IO m, MonadIO m) => ByteString -> ByteString -> Int -> MonarchT m ()
+ Database.Monarch: putShiftLeft :: MonadMonarch m => ByteString -> ByteString -> Int -> m ()
- Database.Monarch: recordNum :: (MonadBaseControl IO m, MonadIO m) => MonarchT m Int64
+ Database.Monarch: recordNum :: MonadMonarch m => m Int64
- Database.Monarch: restore :: (MonadBaseControl IO m, MonadIO m, Integral a) => ByteString -> a -> [RestoreOption] -> MonarchT m ()
+ Database.Monarch: restore :: (MonadMonarch m, Integral a) => ByteString -> a -> [RestoreOption] -> m ()
- Database.Monarch: setMaster :: (MonadBaseControl IO m, MonadIO m, Integral a) => ByteString -> Int -> a -> [RestoreOption] -> MonarchT m ()
+ Database.Monarch: setMaster :: (MonadMonarch m, Integral a) => ByteString -> Int -> a -> [RestoreOption] -> m ()
- Database.Monarch: size :: (MonadBaseControl IO m, MonadIO m) => MonarchT m Int64
+ Database.Monarch: size :: MonadMonarch m => m Int64
- Database.Monarch: status :: (MonadBaseControl IO m, MonadIO m) => MonarchT m ByteString
+ Database.Monarch: status :: MonadMonarch m => m ByteString
- Database.Monarch: sync :: (MonadBaseControl IO m, MonadIO m) => MonarchT m ()
+ Database.Monarch: sync :: MonadMonarch m => m ()
- Database.Monarch: valueSize :: (MonadBaseControl IO m, MonadIO m) => ByteString -> MonarchT m (Maybe Int)
+ Database.Monarch: valueSize :: MonadMonarch m => ByteString -> m (Maybe Int)
- Database.Monarch: vanish :: (MonadBaseControl IO m, MonadIO m) => MonarchT m ()
+ Database.Monarch: vanish :: MonadMonarch m => m ()

Files

− Database/Monarch.hs
@@ -1,30 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}--- | This module provide TokyoTyrant monadic access interface.----module Database.Monarch-    (-      Monarch, MonarchT-    , Connection, ConnectionPool-    , withMonarchConn-    , withMonarchPool-    , runMonarchConn-    , runMonarchPool-    , ExtOption(..), RestoreOption(..), MiscOption(..)-    , Code(..)-    , put, putKeep, putCat, putShiftLeft, multiplePut-    , putNoResponse-    , out, multipleOut-    , get, multipleGet-    , valueSize-    , iterInit, iterNext-    , forwardMatchingKeys-    , addInt, addDouble-    , ext, sync, optimize, vanish, copy, restore-    , setMaster-    , recordNum, size-    , status-    , misc-    ) where--import Database.Monarch.Raw hiding (sendLBS, recvLBS)-import Database.Monarch.Binary
− Database/Monarch/Binary.hs
@@ -1,436 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}--- | TokyoTyrant Original Binary Protocol(<http://fallabs.com/tokyotyrant/spex.html#protocol>).-module Database.Monarch.Binary-    (-      put, putKeep, putCat, putShiftLeft, multiplePut-    , putNoResponse-    , out, multipleOut-    , get, multipleGet-    , valueSize-    , iterInit, iterNext-    , forwardMatchingKeys-    , addInt, addDouble-    , ext, sync, optimize, vanish, copy, restore-    , setMaster-    , recordNum, size-    , status-    , misc-    ) where--import Data.Int-import Data.Maybe-import qualified Data.Binary as B-import Data.Binary.Put (putWord32be, putByteString)-import Data.ByteString.Char8 hiding (length, copy, init, last)-import Control.Applicative-import Control.Monad-import Control.Monad.Error-import Control.Monad.Trans.Control--import Database.Monarch.Raw-import Database.Monarch.Utils---- | Store a record.---   If a record with the same key exists in the database,---   it is overwritten.-put :: ( MonadBaseControl IO m-       , MonadIO m ) =>-       ByteString -- ^ key-    -> ByteString -- ^ value-    -> MonarchT m ()-put key value = communicate request response-    where-      request = do-        putMagic 0x10-        mapM_ (putWord32be . lengthBS32) [key, value]-        mapM_ putByteString [key, value]-      response Success = return ()-      response code = throwError code---- | Store records.---   If a record with the same key exists in the database,---   it is overwritten.-multiplePut :: ( MonadBaseControl IO m-               , MonadIO m ) =>-               [(ByteString,ByteString)] -- ^ key & value pairs-            -> MonarchT m ()-multiplePut [] = return ()-multiplePut kvs = void $ misc "putlist" [] (kvs >>= \(k,v)->[k,v])---- | Store a new record.---   If a record with the same key exists in the database,---   this function has no effect.-putKeep :: ( MonadBaseControl IO m-           , MonadIO m ) =>-           ByteString -- ^ key-        -> ByteString -- ^ value-        -> MonarchT m ()-putKeep key value = communicate request response-    where-      request = do-        putMagic 0x11-        mapM_ (putWord32be . lengthBS32) [key, value]-        mapM_ putByteString [key, value]-      response Success = return ()-      response InvalidOperation = return ()-      response code = throwError code---- | Concatenate a value at the end of the existing record.---   If there is no corresponding record, a new record is created.-putCat :: ( MonadBaseControl IO m-          , MonadIO m ) =>-          ByteString -- ^ key-       -> ByteString -- ^ value-       -> MonarchT m ()-putCat key value = communicate request response-    where-      request = do-        putMagic 0x12-        mapM_ (putWord32be . lengthBS32) [key, value]-        mapM_ putByteString [key, value]-      response Success = return ()-      response code = throwError code---- | Concatenate a value at the end of the existing record and shift it to the left.---   If there is no corresponding record, a new record is created.-putShiftLeft :: ( MonadBaseControl IO m-                , MonadIO m ) =>-                ByteString -- ^ key-             -> ByteString -- ^ value-             -> Int  -- ^ width-             -> MonarchT m ()-putShiftLeft key value width = communicate request response-    where-      request = do-        putMagic 0x13-        mapM_ (putWord32be . lengthBS32) [key, value]-        putWord32be $ fromIntegral width-        mapM_ putByteString [key, value]-      response Success = return ()-      response code = throwError code---- | Store a record without response.---   If a record with the same key exists in the database, it is overwritten.-putNoResponse :: ( MonadBaseControl IO m-                 , MonadIO m ) =>-                 ByteString -- ^ key-              -> ByteString -- ^ value-              -> MonarchT m ()-putNoResponse key value = yieldRequest request-    where-      request = do-        putMagic 0x18-        mapM_ (putWord32be . lengthBS32) [key, value]-        mapM_ putByteString [key, value]---- | Remove a record.-out :: ( MonadBaseControl IO m-       , MonadIO m ) =>-       ByteString -- ^ key-    -> MonarchT m ()-out key = communicate request response-    where-      request = do-        putMagic 0x20-        putWord32be $ lengthBS32 key-        putByteString key-      response Success = return ()-      response InvalidOperation = return ()-      response code = throwError code---- | Remove records.-multipleOut :: ( MonadBaseControl IO m-               , MonadIO m ) =>-               [ByteString] -- ^ keys-            -> MonarchT m ()-multipleOut [] = return ()-multipleOut keys = void $ misc "outlist" [] keys---- | Retrieve a record.-get :: ( MonadBaseControl IO m-       , MonadIO m ) =>-       ByteString -- ^ key-    -> MonarchT m (Maybe ByteString)-get key = communicate request response-    where-      request = do-        putMagic 0x30-        putWord32be $ lengthBS32 key-        putByteString key-      response Success = Just <$> parseBS-      response InvalidOperation = return Nothing-      response code = throwError code---- | Retrieve records.-multipleGet :: ( MonadBaseControl IO m-               , MonadIO m ) =>-               [ByteString] -- ^ keys-            -> MonarchT m [(ByteString, ByteString)]-multipleGet keys = communicate request response-    where-      request = do-        putMagic 0x31-        putWord32be . fromIntegral $ length keys-        mapM_ (\key -> do-                 putWord32be $ lengthBS32 key-                 putByteString key) keys-      response Success = do-          siz <- fromIntegral <$> parseWord32-          replicateM siz parseKeyValue-      response code = throwError code---- | Get the size of the value of a record.-valueSize :: ( MonadBaseControl IO m-             , MonadIO m ) =>-             ByteString -- ^ key-          -> MonarchT m (Maybe Int)-valueSize key = communicate request response-    where-      request = do-        putMagic 0x38-        putWord32be $ lengthBS32 key-        putByteString key-      response Success = Just . fromIntegral <$> parseWord32-      response InvalidOperation = return Nothing-      response code = throwError code---- | Initialize the iterator.-iterInit :: ( MonadBaseControl IO m-            , MonadIO m ) =>-            MonarchT m ()-iterInit = communicate request response-    where-      request = putMagic 0x50-      response Success = return ()-      response code = throwError code---- | Get the next key of the iterator.---   The iterator can be updated by multiple connections and then it is not assured that every record is traversed.-iterNext :: ( MonadBaseControl IO m-            , MonadIO m ) =>-            MonarchT m (Maybe ByteString)-iterNext = communicate request response-    where-      request = putMagic 0x51-      response Success = Just <$> parseBS-      response InvalidOperation = return Nothing-      response code = throwError code---- | Get forward matching keys.-forwardMatchingKeys :: ( MonadBaseControl IO m-                       , MonadIO m ) =>-                       ByteString -- ^ key prefix-                    -> Maybe Int -- ^ maximum number of keys to be fetched. 'Nothing' means unlimited.-                    -> MonarchT m [ByteString]-forwardMatchingKeys prefix n = communicate request response-    where-      request = do-        putMagic 0x58-        putWord32be $ lengthBS32 prefix-        putWord32be $ fromIntegral (fromMaybe (-1) n)-        putByteString prefix-      response Success = do-        siz <- fromIntegral <$> parseWord32-        replicateM siz parseBS-      response code = throwError code---- | Add an integer to a record.---   If the corresponding record exists, the value is treated as an integer and is added to.---   If no record corresponds, a new record of the additional value is stored.-addInt :: ( MonadBaseControl IO m-          , MonadIO m ) =>-          ByteString -- ^ key-       -> Int -- ^ value-       -> MonarchT m Int-addInt key n = communicate request response-    where-      request = do-        putMagic 0x60-        putWord32be $ lengthBS32 key-        putWord32be $ fromIntegral n-        putByteString key-      response Success = fromIntegral <$> parseWord32-      response code = throwError code---- | Add a real number to a record.---   If the corresponding record exists, the value is treated as a real number and is added to.---   If no record corresponds, a new record of the additional value is stored.-addDouble :: ( MonadBaseControl IO m-             , MonadIO m ) =>-             ByteString -- ^ key-          -> Double -- ^ value-          -> MonarchT m Double-addDouble key n = communicate request response-    where-      request = do-        putMagic 0x61-        putWord32be $ lengthBS32 key-        B.put (truncate n :: Int64)-        B.put (truncate (snd (properFraction n :: (Int,Double)) * 1e12) :: Int64)-        putByteString key-      response Success = parseDouble-      response code = throwError code---- | Call a function of the script language extension.-ext :: ( MonadBaseControl IO m-       , MonadIO m ) =>-       ByteString -- ^ function-    -> [ExtOption] -- ^ option flags-    -> ByteString -- ^ key-    -> ByteString -- ^ value-    -> MonarchT m ByteString-ext func opts key value = communicate request response-    where-      request = do-        putMagic 0x68-        putWord32be $ lengthBS32 func-        putOptions opts-        putWord32be $ lengthBS32 key-        putWord32be $ lengthBS32 value-        putByteString func-        putByteString key-        putByteString value-      response Success = parseBS-      response code = throwError code---- | Synchronize updated contents with the file and the device.-sync :: ( MonadBaseControl IO m-        , MonadIO m ) =>-        MonarchT m ()-sync = communicate request response-    where-      request = putMagic 0x70-      response Success = return ()-      response code = throwError code---- | Optimize the storage.-optimize :: ( MonadBaseControl IO m-            , MonadIO m ) =>-            ByteString -- ^ parameter-         -> MonarchT m ()-optimize param = communicate request response-    where-      request = do-        putMagic 0x71-        putWord32be $ lengthBS32 param-        putByteString param-      response Success = return ()-      response code = throwError code---- | Remove all records.-vanish :: ( MonadBaseControl IO m-          , MonadIO m ) =>-          MonarchT m ()-vanish = communicate request response-    where-      request = putMagic 0x72-      response Success = return ()-      response code = throwError code---- | Copy the database file.-copy :: ( MonadBaseControl IO m-        , MonadIO m ) =>-        ByteString -- ^ path-     -> MonarchT m ()-copy path = communicate request response-    where-      request = do-        putMagic 0x73-        putWord32be $ lengthBS32 path-        putByteString path-      response Success = return ()-      response code = throwError code---- | Restore the database file from the update log.-restore :: ( MonadBaseControl IO m-           , MonadIO m-           , Integral a ) =>-           ByteString -- ^ path-        -> a -- ^ beginning time stamp in microseconds-        -> [RestoreOption] -- ^ option flags-        -> MonarchT m ()-restore path usec opts = communicate request response-    where-      request = do-        putMagic 0x74-        putWord32be $ lengthBS32 path-        B.put (fromIntegral usec :: Int64)-        putOptions opts-        putByteString path-      response Success = return ()-      response code = throwError code---- | Set the replication master.-setMaster :: ( MonadBaseControl IO m-             , MonadIO m-             , Integral a ) =>-             ByteString -- ^ host-          -> Int -- ^ port-          -> a -- ^ beginning time stamp in microseconds-          -> [RestoreOption] -- ^ option flags-          -> MonarchT m ()-setMaster host port usec opts = communicate request response-    where-      request = do-        putMagic 0x78-        putWord32be $ lengthBS32 host-        putWord32be $ fromIntegral port-        B.put (fromIntegral usec :: Int64)-        putOptions opts-        putByteString host-      response Success = return ()-      response code = throwError code---- | Get the number of records.-recordNum :: ( MonadBaseControl IO m-             , MonadIO m ) =>-             MonarchT m Int64-recordNum = communicate request response-    where-      request = putMagic 0x80-      response Success = parseInt64-      response code = throwError code---- | Get the size of the database.-size :: ( MonadBaseControl IO m-        , MonadIO m ) =>-        MonarchT m Int64-size = communicate request response-    where-      request = putMagic 0x81-      response Success = parseInt64-      response code = throwError code---- | Get the status string of the database.-status :: ( MonadBaseControl IO m-          , MonadIO m ) =>-          MonarchT m ByteString-status = communicate request response-    where-      request = putMagic 0x88-      response Success = parseBS-      response code = throwError code---- | Call a versatile function for miscellaneous operations.-misc :: ( MonadBaseControl IO m-        , MonadIO m ) =>-        ByteString -- ^ function name-     -> [MiscOption] -- ^ option flags-     -> [ByteString] -- ^ arguments-     -> MonarchT m [ByteString]-misc func opts args = communicate request response-  where-    request = do-      putMagic 0x90-      putWord32be $ lengthBS32 func-      putOptions opts-      putWord32be . fromIntegral $ length args-      putByteString func-      mapM_ (\arg -> do-               putWord32be $ lengthBS32 arg-               putByteString arg) args-    response Success = do-      siz <- fromIntegral <$> parseWord32-      replicateM siz parseBS-    response code = throwError code
− Database/Monarch/Raw.hs
@@ -1,176 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TypeFamilies #-}--- | Raw definitions.-module Database.Monarch.Raw-    (-      Monarch, MonarchT-    , Connection, ConnectionPool-    , withMonarchConn-    , withMonarchPool-    , runMonarchConn-    , runMonarchPool-    , ExtOption(..), RestoreOption(..), MiscOption(..)-    , Code(..)-    , sendLBS, recvLBS-    ) where--import Prelude hiding (catch)-import Data.Int-import Data.Conduit.Pool-import Control.Exception.Lifted-import Control.Monad.Error-import Control.Monad.Reader-import Control.Monad.Base-import Control.Applicative-import Control.Monad.Trans.Control-import Network.Socket-import qualified Network.Socket.ByteString.Lazy as LBS-import qualified Data.ByteString.Lazy as LBS---- | Connection with TokyoTyrant-data Connection = Connection { connection :: Socket }---- | Connection pool with TokyoTyrant-type ConnectionPool = Pool Connection---- | Error code-data Code = Success-          | InvalidOperation-          | HostNotFound-          | ConnectionRefused-          | SendError-          | ReceiveError-          | ExistingRecord-          | NoRecordFound-          | MiscellaneousError-            deriving (Eq, Show)--instance Error Code---- | Options for scripting extension-data ExtOption = RecordLocking -- ^ record locking-               | GlobalLocking -- ^ global locking---- | Options for restore-data RestoreOption = ConsistencyChecking -- ^ consistency checking---- | Options for miscellaneous operation-data MiscOption = NoUpdateLog -- ^ omission of update log---- | The Monarch monad transformer to provide TokyoTyrant access.-newtype MonarchT m a =-    MonarchT { unMonarchT :: ErrorT Code (ReaderT Connection m) a }-    deriving ( Functor, Applicative, Monad, MonadIO-             , MonadReader Connection, MonadError Code, MonadBase base )--instance MonadTrans MonarchT where-    lift = MonarchT . lift . lift--instance MonadTransControl MonarchT where-    newtype StT MonarchT a = StMonarch { unStMonarch :: Either Code a }-    liftWith f = MonarchT . ErrorT . ReaderT $ (\r -> liftM Right (f $ \t -> liftM StMonarch (runReaderT (runErrorT (unMonarchT t)) r)))-    restoreT = MonarchT . ErrorT . ReaderT . const . liftM unStMonarch--instance MonadBaseControl base m => MonadBaseControl base (MonarchT m) where-    newtype StM (MonarchT m) a = StMMonarchT { unStMMonarchT :: ComposeSt MonarchT m a }-    liftBaseWith = defaultLiftBaseWith StMMonarchT-    restoreM = defaultRestoreM unStMMonarchT--type Monarch = MonarchT IO---- | Run Monarch with TokyoTyrant at target host and port.-runMonarch :: MonadIO m =>-              Connection-           -> MonarchT m a-           -> m (Either Code a)-runMonarch conn action =-    runReaderT (runErrorT $ unMonarchT action) conn---- | Create a TokyoTyrant connection and run the given action.--- Don't use the given 'Connection' outside the action.-withMonarchConn :: ( MonadBaseControl IO m-                   , MonadIO m ) =>-                   String -- ^ host-                -> Int -- ^ port-                -> (Connection -> m a)-                -> m a-withMonarchConn host port = bracket open' close'-    where-      open' = liftIO $ getConnection host port-      close' = liftIO . sClose . connection---- | Create a TokyoTyrant connection pool and run the given action.--- Don't use the given 'ConnectionPool' outside the action.-withMonarchPool :: ( MonadBaseControl IO m-                   , MonadIO m ) =>-                   String -- ^ host-                -> Int -- ^ port-                -> Int -- ^ number of connections-                -> (ConnectionPool -> m a)-                -> m a-withMonarchPool host port size f =-    liftIO (createPool open' close' 1 20 size) >>= f-    where-      open' = getConnection host port-      close' = sClose . connection---- | Run action with a connection.-runMonarchConn :: ( MonadBaseControl IO m-                  , MonadIO m ) =>-                  MonarchT m a -- ^ action-               -> Connection -- ^ connection-               -> m (Either Code a)-runMonarchConn action conn = runMonarch conn action---- | Run action with a unused connection from the pool.-runMonarchPool :: ( MonadBaseControl IO m-                  , MonadIO m ) =>-                  MonarchT m a -- ^ action-               -> ConnectionPool -- ^ connection pool-               -> m (Either Code a)-runMonarchPool action pool =-    withResource pool $ flip runMonarch action--throwError' :: Monad m =>-               Code-            -> SomeException-            -> MonarchT m a-throwError' = const . throwError--sendLBS :: ( MonadBaseControl IO m-           , MonadIO m ) =>-           LBS.ByteString-        -> MonarchT m ()-sendLBS lbs = do-  conn <- connection <$> ask-  liftIO (LBS.sendAll conn lbs) `catch` throwError' SendError--recvLBS :: ( MonadBaseControl IO m-           , MonadIO m ) =>-           Int64-        -> MonarchT m LBS.ByteString-recvLBS n = do-  conn <- connection <$> ask-  lbs <- liftIO (LBS.recv conn n) `catch` throwError' ReceiveError-  if LBS.null lbs-    then throwError ReceiveError-    else if n == LBS.length lbs-           then return lbs-           else LBS.append lbs <$> recvLBS (n - LBS.length lbs)--getConnection :: HostName-              -> Int-              -> IO Connection-getConnection host port = do-  let hints = defaultHints { addrFlags = [ AI_ADDRCONFIG ]-                           , addrSocketType = Stream-                           }-  (addr:_) <- getAddrInfo (Just hints) (Just host) (Just $ show port)-  sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)-  let failConnect = (\e -> sClose sock >> throwIO e) :: SomeException -> IO ()-  connect sock (addrAddress addr) `catch` failConnect-  return $ Connection sock
− Database/Monarch/Utils.hs
@@ -1,185 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-module Database.Monarch.Utils-    (-      toCode-    , putMagic, putOptions-    , lengthBS32, lengthLBS32-    , fromLBS-    , yieldRequest-    , responseCode-    , parseLBS, parseBS-    , parseWord32, parseInt64, parseDouble-    , parseKeyValue-    , communicate-    ) where--import Data.Int-import Data.Bits-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.Binary as B-import Data.Binary.Put (runPut, putWord32be)-import Data.Binary.Get (runGet, getWord32be)-import Control.Applicative-import Control.Monad.IO.Class-import Control.Monad.Trans.Control--import Database.Monarch.Raw--class BitFlag32 a where-    fromOption :: a -> Int32--instance BitFlag32 ExtOption where-    fromOption RecordLocking = 0x1-    fromOption GlobalLocking = 0x2--instance BitFlag32 RestoreOption where-    fromOption ConsistencyChecking = 0x1--instance BitFlag32 MiscOption where-    fromOption NoUpdateLog = 0x1--toCode :: Int -> Code-toCode 0 = Success-toCode 1 = InvalidOperation-toCode 2 = HostNotFound-toCode 3 = ConnectionRefused-toCode 4 = SendError-toCode 5 = ReceiveError-toCode 6 = ExistingRecord-toCode 7 = NoRecordFound-toCode 9999 = MiscellaneousError-toCode _ = error "Invalid Code"---- | TokyoTyrant Original Binary Protocal magic id.------ Example:------ >>> :m +Data.ByteString.Char8--- >>> :set -XOverloadedStrings--- >>> fromLBS (runPut $ putMagic 0x10) == "\xC8\x10"--- True----putMagic :: B.Word8 -> B.Put-putMagic magic = B.putWord8 0xC8 >> B.putWord8 magic---- | Option------ Example:------ >>> :m +Data.ByteString.Char8--- >>> :set -XOverloadedStrings--- >>> fromLBS (runPut $ putOptions [RecordLocking]) == "\0\0\0\1"--- True--- >>> fromLBS (runPut $ putOptions [GlobalLocking]) == "\0\0\0\2"--- True--- >>> fromLBS (runPut $ putOptions [RecordLocking, GlobalLocking]) == "\0\0\0\3"--- True----putOptions :: BitFlag32 option =>-              [option]-           -> B.Put-putOptions = putWord32be . fromIntegral .-             foldl (.|.) 0 . map fromOption---- | Get Length------ Example:------ >>> :m +Data.ByteString.Char8--- >>> :set -XOverloadedStrings--- >>> lengthBS32 "test"--- 4--- >>> lengthBS32 ""--- 0----lengthBS32 :: BS.ByteString -> B.Word32-lengthBS32 = fromIntegral . BS.length---- | Get Length------ Example:------ >>> :m +Data.ByteString.Lazy.Char8--- >>> :set -XOverloadedStrings--- >>> lengthLBS32 "test"--- 4--- >>> lengthLBS32 ""--- 0----lengthLBS32 :: LBS.ByteString -> B.Word32-lengthLBS32 = fromIntegral . LBS.length---- | Convert------ Example:------ >>> :m +Data.ByteString.Lazy.Char8--- >>> :set -XOverloadedStrings--- >>> fromLBS "test"--- "test"----fromLBS :: LBS.ByteString -> BS.ByteString-fromLBS = BS.pack . LBS.unpack--yieldRequest :: ( MonadBaseControl IO m-                , MonadIO m ) =>-                B.Put-             -> MonarchT m ()-yieldRequest = sendLBS . runPut--responseCode :: ( MonadBaseControl IO m-                , MonadIO m ) =>-                MonarchT m Code-responseCode = toCode . fromIntegral . runGet B.getWord8 <$> recvLBS 1--parseLBS :: ( MonadBaseControl IO m-            , MonadIO m ) =>-            MonarchT m LBS.ByteString-parseLBS = recvLBS 4 >>=-           recvLBS . fromIntegral . runGet getWord32be--parseBS :: ( MonadBaseControl IO m-           , MonadIO m ) =>-           MonarchT m BS.ByteString-parseBS = fromLBS <$> parseLBS--parseWord32 :: ( MonadBaseControl IO m-               , MonadIO m ) =>-               MonarchT m B.Word32-parseWord32 = runGet getWord32be <$> recvLBS 4--parseInt64 :: ( MonadBaseControl IO m-              , MonadIO m ) =>-              MonarchT m Int64-parseInt64 = runGet (B.get :: B.Get Int64) <$> recvLBS 8--parseDouble :: ( MonadBaseControl IO m-               , MonadIO m ) =>-               MonarchT m Double-parseDouble = do-  integ <- fromIntegral <$> parseInt64-  fract <- fromIntegral <$> parseInt64-  return $ integ + fract * 1e-12--parseKeyValue :: ( MonadBaseControl IO m-                 , MonadIO m ) =>-                 MonarchT m (BS.ByteString, BS.ByteString)-parseKeyValue = do-  ksiz <- recvLBS 4-  vsiz <- recvLBS 4-  key <- recvLBS . fromIntegral $-         runGet getWord32be ksiz-  value <- recvLBS . fromIntegral $-           runGet getWord32be vsiz-  return (fromLBS key, fromLBS value)--communicate :: ( MonadBaseControl IO m-               , MonadIO m) =>-               B.Put-            -> (Code -> MonarchT m a)-            -> MonarchT m a-communicate makeRequest makeResponse =-    yieldRequest makeRequest >>-    responseCode >>=-    makeResponse
monarch.cabal view
@@ -1,5 +1,5 @@ name:                monarch-version:             0.8.1.3+version:             0.9.0.0 synopsis:            Monadic interface for TokyoTyrant. description:         This package provides simple monadic interface for TokyoTyrant. license:             BSD3@@ -10,7 +10,7 @@ build-type:          Simple cabal-version:       >=1.8 homepage:            https://github.com/notogawa/monarch-tested-with:         GHC ==7.4.2+tested-with:         GHC ==7.4.1, GHC ==7.6.3  source-repository head   type:                git@@ -21,69 +21,63 @@   default:             False  library+  hs-source-dirs:      src   exposed-modules:     Database.Monarch+                       Database.Monarch.Mock   other-modules:       Database.Monarch.Utils-                     , Database.Monarch.Raw-                     , Database.Monarch.Binary+                       Database.Monarch.Types+                       Database.Monarch.Action+                       Database.Monarch.Mock.Types+                       Database.Monarch.Mock.Action   ghc-options:         -Wall   build-depends:       base ==4.*                      , mtl ==2.1.*                      , transformers ==0.3.*-                     , bytestring ==0.9.*+                     , bytestring >=0.9 && <0.11                      , binary ==0.5.*                      , transformers ==0.3.*-                     , network ==2.3.*+                     , network >=2.3 && <2.5                      , pool-conduit ==0.1.*                      , monad-control ==0.3.*-                     , lifted-base ==0.1.*+                     , lifted-base >=0.1 && <0.3                      , transformers-base ==0.4.*+                     , containers >=0.4 && <0.6+                     , stm >=2.3 && <2.5  test-suite specs   if flag(develop)     buildable:         True   else     buildable:         False-  hs-source-dirs:      test, .+  hs-source-dirs:      test   type:                exitcode-stdio-1.0-  main-is:             specs.hs+  ghc-options:         -Wall -threaded+  main-is:             Spec.hs+  other-modules:       Database.Monarch.ActionSpec+                       Database.Monarch.Mock.ActionSpec   build-depends:       base ==4.*-                     , mtl ==2.1.*-                     , transformers ==0.3.*-                     , bytestring ==0.9.*-                     , binary ==0.5.*-                     , network ==2.3.*-                     , pool-conduit ==0.1.*-                     , monad-control ==0.3.*-                     , lifted-base ==0.1.*-                     , transformers-base ==0.4.*-                     , hspec ==1.3.*-                     , HUnit ==1.2.*+                     , monarch+                     , bytestring+                     , transformers+                     , hspec >=1.3  test-suite doctests-  hs-source-dirs:      test, .+  hs-source-dirs:      test   type:                exitcode-stdio-1.0   main-is:             doctests.hs   build-depends:       base ==4.*-                     , doctest ==0.8.*+                     , doctest  benchmark benchmark   if flag(develop)     buildable:         True   else     buildable:         False-  hs-source-dirs:      test, .+  hs-source-dirs:      test   type:                exitcode-stdio-1.0   main-is:             benchmark.hs   build-depends:       base ==4.*-                     , mtl ==2.1.*-                     , transformers ==0.3.*-                     , bytestring ==0.9.*-                     , binary ==0.5.*-                     , network ==2.3.*-                     , pool-conduit ==0.1.*-                     , monad-control ==0.3.*-                     , lifted-base ==0.1.*-                     , tokyotyrant-haskell ==1.0.*-                     , transformers ==0.3.*-                     , transformers-base ==0.4.*-                     , criterion ==0.6.*+                     , monarch+                     , tokyotyrant-haskell+                     , bytestring+                     , criterion
+ src/Database/Monarch.hs view
@@ -0,0 +1,26 @@+-- |+-- Module      : Database.Monarch+-- Copyright   : 2013 Noriyuki OHKAWA+-- License     : BSD3+--+-- Maintainer  : n.ohkawa@gmail.com+-- Stability   : experimental+-- Portability : unknown+--+-- Provide TokyoTyrant monadic access interface.+--+module Database.Monarch+    (+      Monarch, MonarchT+    , Connection, ConnectionPool+    , withMonarchConn+    , withMonarchPool+    , runMonarchConn+    , runMonarchPool+    , ExtOption(..), RestoreOption(..), MiscOption(..)+    , Code(..)+    , MonadMonarch(..)+    ) where++import Database.Monarch.Types hiding (sendLBS, recvLBS)+import Database.Monarch.Action ()
+ src/Database/Monarch/Action.hs view
@@ -0,0 +1,247 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+-- |+-- Module      : Database.Monarch.Action+-- Copyright   : 2013 Noriyuki OHKAWA+-- License     : BSD3+--+-- Maintainer  : n.ohkawa@gmail.com+-- Stability   : experimental+-- Portability : unknown+--+-- TokyoTyrant Original Binary Protocol(<http://fallabs.com/tokyotyrant/spex.html#protocol>).+--+module Database.Monarch.Action () where++import Data.Int+import Data.Maybe+import qualified Data.Binary as B+import Data.Binary.Put (putWord32be, putByteString)+import Data.ByteString.Char8 ()+import Control.Applicative+import Control.Monad+import Control.Monad.Error+import Control.Monad.Trans.Control++import Database.Monarch.Types+import Database.Monarch.Utils++instance ( MonadBaseControl IO m, MonadIO m ) => MonadMonarch (MonarchT m) where++    put key value = communicate request response where+        request = do+          putMagic 0x10+          mapM_ (putWord32be . lengthBS32) [key, value]+          mapM_ putByteString [key, value]+        response Success = return ()+        response code = throwError code++    multiplePut [] = return ()+    multiplePut kvs = void $ misc "putlist" [] (kvs >>= \(k,v)->[k,v])++    putKeep key value = communicate request response where+        request = do+          putMagic 0x11+          mapM_ (putWord32be . lengthBS32) [key, value]+          mapM_ putByteString [key, value]+        response Success = return ()+        response InvalidOperation = return ()+        response code = throwError code++    putCat key value = communicate request response where+        request = do+          putMagic 0x12+          mapM_ (putWord32be . lengthBS32) [key, value]+          mapM_ putByteString [key, value]+        response Success = return ()+        response code = throwError code++    putShiftLeft key value width = communicate request response where+        request = do+          putMagic 0x13+          mapM_ (putWord32be . lengthBS32) [key, value]+          putWord32be $ fromIntegral width+          mapM_ putByteString [key, value]+        response Success = return ()+        response code = throwError code++    putNoResponse key value = yieldRequest request where+        request = do+          putMagic 0x18+          mapM_ (putWord32be . lengthBS32) [key, value]+          mapM_ putByteString [key, value]++    out key = communicate request response where+        request = do+          putMagic 0x20+          putWord32be $ lengthBS32 key+          putByteString key+        response Success = return ()+        response InvalidOperation = return ()+        response code = throwError code++    multipleOut [] = return ()+    multipleOut keys = void $ misc "outlist" [] keys++    get key = communicate request response where+        request = do+          putMagic 0x30+          putWord32be $ lengthBS32 key+          putByteString key+        response Success = Just <$> parseBS+        response InvalidOperation = return Nothing+        response code = throwError code++    multipleGet keys = communicate request response where+        request = do+          putMagic 0x31+          putWord32be . fromIntegral $ length keys+          mapM_ (\key -> do+                   putWord32be $ lengthBS32 key+                   putByteString key) keys+        response Success = do+            siz <- fromIntegral <$> parseWord32+            replicateM siz parseKeyValue+        response code = throwError code++    valueSize key = communicate request response where+        request = do+          putMagic 0x38+          putWord32be $ lengthBS32 key+          putByteString key+        response Success = Just . fromIntegral <$> parseWord32+        response InvalidOperation = return Nothing+        response code = throwError code++    iterInit = communicate request response where+        request = putMagic 0x50+        response Success = return ()+        response code = throwError code++    iterNext = communicate request response where+        request = putMagic 0x51+        response Success = Just <$> parseBS+        response InvalidOperation = return Nothing+        response code = throwError code++    forwardMatchingKeys prefix n = communicate request response where+        request = do+          putMagic 0x58+          putWord32be $ lengthBS32 prefix+          putWord32be $ fromIntegral (fromMaybe (-1) n)+          putByteString prefix+        response Success = do+          siz <- fromIntegral <$> parseWord32+          replicateM siz parseBS+        response code = throwError code++    addInt key n = communicate request response where+        request = do+          putMagic 0x60+          putWord32be $ lengthBS32 key+          putWord32be $ fromIntegral n+          putByteString key+        response Success = fromIntegral <$> parseWord32+        response code = throwError code++    addDouble key n = communicate request response where+        request = do+          putMagic 0x61+          putWord32be $ lengthBS32 key+          B.put (truncate n :: Int64)+          B.put (truncate (snd (properFraction n :: (Int,Double)) * 1e12) :: Int64)+          putByteString key+        response Success = parseDouble+        response code = throwError code++    ext func opts key value = communicate request response where+        request = do+          putMagic 0x68+          putWord32be $ lengthBS32 func+          putOptions opts+          putWord32be $ lengthBS32 key+          putWord32be $ lengthBS32 value+          putByteString func+          putByteString key+          putByteString value+        response Success = parseBS+        response code = throwError code++    sync = communicate request response where+        request = putMagic 0x70+        response Success = return ()+        response code = throwError code++    optimize param = communicate request response where+        request = do+          putMagic 0x71+          putWord32be $ lengthBS32 param+          putByteString param+        response Success = return ()+        response code = throwError code++    vanish = communicate request response where+        request = putMagic 0x72+        response Success = return ()+        response code = throwError code++    copy path = communicate request response where+        request = do+          putMagic 0x73+          putWord32be $ lengthBS32 path+          putByteString path+        response Success = return ()+        response code = throwError code++    restore path usec opts = communicate request response where+        request = do+          putMagic 0x74+          putWord32be $ lengthBS32 path+          B.put (fromIntegral usec :: Int64)+          putOptions opts+          putByteString path+        response Success = return ()+        response code = throwError code++    setMaster host port usec opts = communicate request response where+        request = do+          putMagic 0x78+          putWord32be $ lengthBS32 host+          putWord32be $ fromIntegral port+          B.put (fromIntegral usec :: Int64)+          putOptions opts+          putByteString host+        response Success = return ()+        response code = throwError code++    recordNum = communicate request response where+        request = putMagic 0x80+        response Success = parseInt64+        response code = throwError code++    size = communicate request response where+        request = putMagic 0x81+        response Success = parseInt64+        response code = throwError code++    status = communicate request response where+        request = putMagic 0x88+        response Success = parseBS+        response code = throwError code++    misc func opts args = communicate request response where+        request = do+          putMagic 0x90+          putWord32be $ lengthBS32 func+          putOptions opts+          putWord32be . fromIntegral $ length args+          putByteString func+          mapM_ (\arg -> do+                   putWord32be $ lengthBS32 arg+                   putByteString arg) args+        response Success = do+          siz <- fromIntegral <$> parseWord32+          replicateM siz parseBS+        response code = throwError code
+ src/Database/Monarch/Mock.hs view
@@ -0,0 +1,19 @@+-- |+-- Module      : Database.Monarch.Mock+-- Copyright   : 2013 Noriyuki OHKAWA+-- License     : BSD3+--+-- Maintainer  : n.ohkawa@gmail.com+-- Stability   : experimental+-- Portability : unknown+--+-- Provide TokyoTyrant mock.+--+module Database.Monarch.Mock+    ( MockT, MockDB+    , newMockDB+    , runMock+    ) where++import Database.Monarch.Mock.Types+import Database.Monarch.Mock.Action ()
+ src/Database/Monarch/Mock/Action.hs view
@@ -0,0 +1,166 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+-- |+-- Module      : Database.Monarch.Mock.Types+-- Copyright   : 2013 Noriyuki OHKAWA+-- License     : BSD3+--+-- Maintainer  : n.ohkawa@gmail.com+-- Stability   : experimental+-- Portability : unknown+--+-- Mock actions.+--+module Database.Monarch.Mock.Action () where++import Control.Concurrent.STM.TVar+import Control.Monad.Reader+import Control.Monad.STM ( atomically )+import Control.Monad.Trans.Control+import Database.Monarch.Types ( MonadMonarch(..) )+import Database.Monarch.Mock.Types ( MockT, MockDB, mockDB, emptyMockDB, TTValue(..) )+import qualified Data.ByteString as BS+import qualified Data.Map as M+import Data.Map ( (!) )+import Data.Monoid ( (<>) )++putDB :: BS.ByteString -> TTValue -> MockDB -> MockDB+putDB key value db = db { mockDB = M.insert key value (mockDB db) }++putDBS :: BS.ByteString -> BS.ByteString -> MockDB -> MockDB+putDBS key value = putDB key (TTString value)++putDBI :: BS.ByteString -> Int -> MockDB -> MockDB+putDBI key value = putDB key (TTInt value)++putDBD :: BS.ByteString -> Double -> MockDB -> MockDB+putDBD key value = putDB key (TTDouble value)++getDB :: BS.ByteString -> MockDB -> Maybe BS.ByteString+getDB key db = M.lookup key (mockDB db) >>= \mvalue ->+               case mvalue of+                 TTString value -> return value+                 _              -> error "get"++instance ( MonadBaseControl IO m, MonadIO m ) => MonadMonarch (MockT m) where++    put key value = do+      tdb <- ask+      liftIO $ atomically $ modifyTVar tdb $ putDBS key value++    multiplePut = mapM_ (uncurry put)++    putKeep key value = do+      tdb <- ask+      let modify db+              | M.member key (mockDB db) = db+              | otherwise              = putDBS key value db+      liftIO $ atomically $ modifyTVar tdb modify++    putCat key value = do+      tdb <- ask+      let modify db+              | M.member key (mockDB db) =+                  case mockDB db ! key of+                    TTString v -> putDBS key (v <> value) db+                    _          -> error "putCat"+              | otherwise                =+                  putDBS key value db+      liftIO $ atomically $ modifyTVar tdb modify++    putShiftLeft key value width = do+      tdb <- ask+      let modify db+              | M.member key (mockDB db) =+                  case mockDB db ! key of+                    TTString v -> putDBS key (BS.drop (BS.length (v <> value) - width) $ v <> value) db+                    _          -> error "putShiftLeft"+              | otherwise              =+                  putDBS key value db+      liftIO $ atomically $ modifyTVar tdb modify++    putNoResponse = put++    out key = do+      tdb <- ask+      let modify db = db { mockDB = M.delete key (mockDB db) }+      liftIO $ atomically $ modifyTVar tdb modify++    multipleOut = mapM_ out++    get key = do+      tdb <- ask+      liftIO $ atomically $ fmap (getDB key) $ readTVar tdb++    multipleGet keys = do+      vs <- mapM (\k -> fmap (\v -> (k, v)) $ get k) keys+      return [ (k, v) | (k, Just v) <- vs]++    valueSize = fmap (fmap BS.length) . get++    iterInit = return ()++    iterNext = error "not implemented"++    forwardMatchingKeys prefix n = do+      tdb <- ask+      let readKeys db = filter (BS.isPrefixOf prefix) $ M.keys $ mockDB db+      ks <- liftIO $ atomically $ fmap readKeys $ readTVar tdb+      case n of+        Nothing -> return ks+        Just x -> return $ take x ks++    addInt key n = do+      tdb <- ask+      let modify db+              | M.member key (mockDB db) =+                  case mockDB db ! key of+                    TTInt x -> putDBI key (x + n) db+                    _       -> error "addInt"+              | otherwise                =+                  putDBI key n db+      let readDouble db = case mockDB db ! key of+                            TTInt x -> x+                            _       -> error "addInt"+      liftIO $ atomically $ modifyTVar tdb modify >> fmap readDouble (readTVar tdb)++    addDouble key n = do+      tdb <- ask+      let modify db+              | M.member key (mockDB db) =+                  case mockDB db ! key of+                    TTDouble x -> putDBD key (x + n) db+                    _          -> error "addDouble"+              | otherwise                =+                  putDBD key n db+      let readDouble db = case mockDB db ! key of+                            TTDouble x -> x+                            _          -> error "addDouble"+      liftIO $ atomically $ modifyTVar tdb modify >> fmap readDouble (readTVar tdb)++    ext _func _opts _key _value = error "not implemented"++    sync = error "not implemented"++    optimize _param = return ()++    vanish = do+      tdb <- ask+      liftIO $ atomically $ modifyTVar tdb $ const emptyMockDB++    copy _path = error "not implemented"++    restore _path _usec _opts = error "not implemented"++    setMaster _host _port _usec _opts = error "not implemented"++    recordNum = do+      tdb <- ask+      liftIO $ atomically $ fmap (toEnum . M.size . mockDB) $ readTVar tdb++    size = error "not implemented"++    status = error "not implemented"++    misc _func _opts _args = error "not implemented"
+ src/Database/Monarch/Mock/Types.hs view
@@ -0,0 +1,79 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies #-}+-- |+-- Module      : Database.Monarch.Mock.Types+-- Copyright   : 2013 Noriyuki OHKAWA+-- License     : BSD3+--+-- Maintainer  : n.ohkawa@gmail.com+-- Stability   : experimental+-- Portability : unknown+--+-- Type definitions.+--+module Database.Monarch.Mock.Types+    (+      MockT+    , MockDB, mockDB+    , newMockDB, emptyMockDB+    , runMock+    , TTValue(..)+    ) where++import Control.Concurrent.STM.TVar+import Control.Monad.Error+import Control.Monad.Reader+import Control.Monad.Base+import Control.Applicative+import Control.Monad.Trans.Control+import qualified Data.ByteString as BS+import qualified Data.Map as M++import Database.Monarch.Types ( Code )++-- | KVS Value type+data TTValue = TTString BS.ByteString+             | TTInt Int+             | TTDouble Double++-- | Connection with TokyoTyrant+data MockDB = MockDB { mockDB :: M.Map BS.ByteString TTValue -- ^ DB+                     }++-- | The Mock monad transformer to provide TokyoTyrant access.+newtype MockT m a =+    MockT { unMockT :: ErrorT Code (ReaderT (TVar MockDB) m) a }+    deriving ( Functor, Applicative, Monad, MonadIO+             , MonadReader (TVar MockDB), MonadError Code, MonadBase base )++instance MonadTrans MockT where+    lift = MockT . lift . lift++instance MonadTransControl MockT where+    newtype StT MockT a = StMock { unStMock :: Either Code a }+    liftWith f = MockT . ErrorT . ReaderT $ (\r -> liftM Right (f $ \t -> liftM StMock (runReaderT (runErrorT (unMockT t)) r)))+    restoreT = MockT . ErrorT . ReaderT . const . liftM unStMock++instance MonadBaseControl base m => MonadBaseControl base (MockT m) where+    newtype StM (MockT m) a = StMMockT { unStMMockT :: ComposeSt MockT m a }+    liftBaseWith = defaultLiftBaseWith StMMockT+    restoreM = defaultRestoreM unStMMockT++-- | Empty mock DB+emptyMockDB :: MockDB+emptyMockDB = MockDB { mockDB = M.empty }++-- | Create mock DB+newMockDB :: IO (TVar MockDB)+newMockDB = newTVarIO emptyMockDB++-- | Run Mock with TokyoTyrant at target host and port.+runMock :: MonadIO m =>+           MockT m a+        -> TVar MockDB+        -> m (Either Code a)+runMock action =+    runReaderT (runErrorT $ unMockT action)
+ src/Database/Monarch/Types.hs view
@@ -0,0 +1,333 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies #-}+-- |+-- Module      : Database.Monarch.Types+-- Copyright   : 2013 Noriyuki OHKAWA+-- License     : BSD3+--+-- Maintainer  : n.ohkawa@gmail.com+-- Stability   : experimental+-- Portability : unknown+--+-- Type definitions.+--+module Database.Monarch.Types+    (+      Monarch, MonarchT+    , Connection, ConnectionPool+    , withMonarchConn+    , withMonarchPool+    , runMonarchConn+    , runMonarchPool+    , ExtOption(..), RestoreOption(..), MiscOption(..)+    , Code(..)+    , sendLBS, recvLBS+    , MonadMonarch(..)+    ) where++import Prelude hiding (catch)+import Data.Int+import Data.Conduit.Pool+import Control.Exception.Lifted+import Control.Monad.Error+import Control.Monad.Reader+import Control.Monad.Base+import Control.Applicative+import Control.Monad.Trans.Control+import Network.Socket+import qualified Network.Socket.ByteString.Lazy as LBS+import qualified Data.ByteString.Lazy as LBS+import qualified Data.ByteString as BS++-- | Connection with TokyoTyrant+data Connection = Connection { connection :: Socket }++-- | Connection pool with TokyoTyrant+type ConnectionPool = Pool Connection++-- | Error code+data Code = Success+          | InvalidOperation+          | HostNotFound+          | ConnectionRefused+          | SendError+          | ReceiveError+          | ExistingRecord+          | NoRecordFound+          | MiscellaneousError+            deriving (Eq, Show)++instance Error Code++-- | Options for scripting extension+data ExtOption = RecordLocking -- ^ record locking+               | GlobalLocking -- ^ global locking++-- | Options for restore+data RestoreOption = ConsistencyChecking -- ^ consistency checking++-- | Options for miscellaneous operation+data MiscOption = NoUpdateLog -- ^ omission of update log++-- | The Monarch monad transformer to provide TokyoTyrant access.+newtype MonarchT m a =+    MonarchT { unMonarchT :: ErrorT Code (ReaderT Connection m) a }+    deriving ( Functor, Applicative, Monad, MonadIO+             , MonadReader Connection, MonadError Code, MonadBase base )++instance MonadTrans MonarchT where+    lift = MonarchT . lift . lift++instance MonadTransControl MonarchT where+    newtype StT MonarchT a = StMonarch { unStMonarch :: Either Code a }+    liftWith f = MonarchT . ErrorT . ReaderT $ (\r -> liftM Right (f $ \t -> liftM StMonarch (runReaderT (runErrorT (unMonarchT t)) r)))+    restoreT = MonarchT . ErrorT . ReaderT . const . liftM unStMonarch++instance MonadBaseControl base m => MonadBaseControl base (MonarchT m) where+    newtype StM (MonarchT m) a = StMMonarchT { unStMMonarchT :: ComposeSt MonarchT m a }+    liftBaseWith = defaultLiftBaseWith StMMonarchT+    restoreM = defaultRestoreM unStMMonarchT++-- | IO Specialized+type Monarch = MonarchT IO++-- | Run Monarch with TokyoTyrant at target host and port.+runMonarch :: MonadIO m =>+              Connection+           -> MonarchT m a+           -> m (Either Code a)+runMonarch conn action =+    runReaderT (runErrorT $ unMonarchT action) conn++-- | Create a TokyoTyrant connection and run the given action.+-- Don't use the given 'Connection' outside the action.+withMonarchConn :: ( MonadBaseControl IO m+                   , MonadIO m ) =>+                   String -- ^ host+                -> Int -- ^ port+                -> (Connection -> m a)+                -> m a+withMonarchConn host port = bracket open' close'+    where+      open' = liftIO $ getConnection host port+      close' = liftIO . sClose . connection++-- | Create a TokyoTyrant connection pool and run the given action.+-- Don't use the given 'ConnectionPool' outside the action.+withMonarchPool :: ( MonadBaseControl IO m+                   , MonadIO m ) =>+                   String -- ^ host+                -> Int -- ^ port+                -> Int -- ^ number of connections+                -> (ConnectionPool -> m a)+                -> m a+withMonarchPool host port connections f =+    liftIO (createPool open' close' 1 20 connections) >>= f+    where+      open' = getConnection host port+      close' = sClose . connection++-- | Run action with a connection.+runMonarchConn :: ( MonadBaseControl IO m+                  , MonadIO m ) =>+                  MonarchT m a -- ^ action+               -> Connection -- ^ connection+               -> m (Either Code a)+runMonarchConn action conn = runMonarch conn action++-- | Run action with a unused connection from the pool.+runMonarchPool :: ( MonadBaseControl IO m+                  , MonadIO m ) =>+                  MonarchT m a -- ^ action+               -> ConnectionPool -- ^ connection pool+               -> m (Either Code a)+runMonarchPool action pool =+    withResource pool $ flip runMonarch action++throwError' :: Monad m =>+             Code+          -> SomeException+          -> MonarchT m a+throwError' = const . throwError++-- | Send.+sendLBS :: ( MonadBaseControl IO m+           , MonadIO m ) =>+           LBS.ByteString+        -> MonarchT m ()+sendLBS lbs = do+  conn <- connection <$> ask+  liftIO (LBS.sendAll conn lbs) `catch` throwError' SendError++-- | Receive.+recvLBS :: ( MonadBaseControl IO m+           , MonadIO m ) =>+           Int64+        -> MonarchT m LBS.ByteString+recvLBS n = do+  conn <- connection <$> ask+  lbs <- liftIO (LBS.recv conn n) `catch` throwError' ReceiveError+  if LBS.null lbs+    then throwError ReceiveError+    else if n == LBS.length lbs+           then return lbs+           else LBS.append lbs <$> recvLBS (n - LBS.length lbs)++-- | Make connection from host and port.+getConnection :: HostName+              -> Int+              -> IO Connection+getConnection host port = do+  let hints = defaultHints { addrFlags = [ AI_ADDRCONFIG ]+                           , addrSocketType = Stream+                           }+  (addr:_) <- getAddrInfo (Just hints) (Just host) (Just $ show port)+  sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)+  let failConnect = (\e -> sClose sock >> throwIO e) :: SomeException -> IO ()+  connect sock (addrAddress addr) `catch` failConnect+  return $ Connection sock++-- | Monad Monarch interfaces+class MonadMonarch m where++  -- | Store a record.+  --   If a record with the same key exists in the database,+  --   it is overwritten.+  put :: BS.ByteString -- ^ key+      -> BS.ByteString -- ^ value+      -> m ()++  -- | Store records.+  --   If a record with the same key exists in the database,+  --   it is overwritten.+  multiplePut :: [(BS.ByteString,BS.ByteString)] -- ^ key & value pairs+              -> m ()++  -- | Store a new record.+  --   If a record with the same key exists in the database,+  --   this function has no effect.+  putKeep :: BS.ByteString -- ^ key+          -> BS.ByteString -- ^ value+          -> m ()++  -- | Concatenate a value at the end of the existing record.+  --   If there is no corresponding record, a new record is created.+  putCat :: BS.ByteString -- ^ key+         -> BS.ByteString -- ^ value+         -> m ()++  -- | Concatenate a value at the end of the existing record and shift it to the left.+  --   If there is no corresponding record, a new record is created.+  putShiftLeft :: BS.ByteString -- ^ key+               -> BS.ByteString -- ^ value+               -> Int  -- ^ width+               -> m ()++  -- | Store a record without response.+  --   If a record with the same key exists in the database, it is overwritten.+  putNoResponse :: BS.ByteString -- ^ key+                -> BS.ByteString -- ^ value+                -> m ()++  -- | Remove a record.+  out :: ( MonadBaseControl IO m+         , MonadIO m ) =>+         BS.ByteString -- ^ key+      -> m ()++  -- | Remove records.+  multipleOut :: [BS.ByteString] -- ^ keys+              -> m ()++  -- | Retrieve a record.+  get :: BS.ByteString -- ^ key+      -> m (Maybe BS.ByteString)++  -- | Retrieve records.+  multipleGet :: [BS.ByteString] -- ^ keys+              -> m [(BS.ByteString, BS.ByteString)]++  -- | Get the size of the value of a record.+  valueSize :: BS.ByteString -- ^ key+            -> m (Maybe Int)++  -- | Initialize the iterator.+  iterInit :: m ()++  -- | Get the next key of the iterator.+  --   The iterator can be updated by multiple connections and then it is not assured that every record is traversed.+  iterNext :: m (Maybe BS.ByteString)++  -- | Get forward matching keys.+  forwardMatchingKeys :: BS.ByteString -- ^ key prefix+                      -> Maybe Int -- ^ maximum number of keys to be fetched. 'Nothing' means unlimited.+                      -> m [BS.ByteString]++  -- | Add an integer to a record.+  --   If the corresponding record exists, the value is treated as an integer and is added to.+  --   If no record corresponds, a new record of the additional value is stored.+  addInt :: BS.ByteString -- ^ key+         -> Int -- ^ value+         -> m Int++  -- | Add a real number to a record.+  --   If the corresponding record exists, the value is treated as a real number and is added to.+  --   If no record corresponds, a new record of the additional value is stored.+  addDouble :: BS.ByteString -- ^ key+            -> Double -- ^ value+            -> m Double++  -- | Call a function of the script language extension.+  ext :: BS.ByteString -- ^ function+      -> [ExtOption] -- ^ option flags+      -> BS.ByteString -- ^ key+      -> BS.ByteString -- ^ value+      -> m BS.ByteString++  -- | Synchronize updated contents with the file and the device.+  sync :: m ()++  -- | Optimize the storage.+  optimize :: BS.ByteString -- ^ parameter+           -> m ()++  -- | Remove all records.+  vanish :: m ()++  -- | Copy the database file.+  copy :: BS.ByteString -- ^ path+       -> m ()++  -- | Restore the database file from the update log.+  restore :: ( Integral a ) =>+             BS.ByteString -- ^ path+          -> a -- ^ beginning time stamp in microseconds+          -> [RestoreOption] -- ^ option flags+          -> m ()++  -- | Set the replication master.+  setMaster :: ( Integral a ) =>+               BS.ByteString -- ^ host+            -> Int -- ^ port+            -> a -- ^ beginning time stamp in microseconds+            -> [RestoreOption] -- ^ option flags+            -> m ()++  -- | Get the number of records.+  recordNum :: m Int64++  -- | Get the size of the database.+  size :: m Int64++  -- | Get the status string of the database.+  status :: m BS.ByteString++  -- | Call a versatile function for miscellaneous operations.+  misc :: BS.ByteString -- ^ function name+       -> [MiscOption] -- ^ option flags+       -> [BS.ByteString] -- ^ arguments+       -> m [BS.ByteString]
+ src/Database/Monarch/Utils.hs view
@@ -0,0 +1,206 @@+{-# LANGUAGE FlexibleContexts #-}+-- |+-- Module      : Database.Monarch.Utils+-- Copyright   : 2013 Noriyuki OHKAWA+-- License     : BSD3+--+-- Maintainer  : n.ohkawa@gmail.com+-- Stability   : experimental+-- Portability : unknown+--+-- Internal utilities.+--+module Database.Monarch.Utils+    (+      toCode+    , putMagic, putOptions+    , lengthBS32, lengthLBS32+    , fromLBS+    , yieldRequest+    , responseCode+    , parseLBS, parseBS+    , parseWord32, parseInt64, parseDouble+    , parseKeyValue+    , communicate+    ) where++import Data.Int+import Data.Bits+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as LBS+import qualified Data.Binary as B+import Data.Binary.Put (runPut, putWord32be)+import Data.Binary.Get (runGet, getWord32be)+import Control.Applicative+import Control.Monad.IO.Class+import Control.Monad.Trans.Control++import Database.Monarch.Types++class BitFlag32 a where+    fromOption :: a -> Int32++instance BitFlag32 ExtOption where+    fromOption RecordLocking = 0x1+    fromOption GlobalLocking = 0x2++instance BitFlag32 RestoreOption where+    fromOption ConsistencyChecking = 0x1++instance BitFlag32 MiscOption where+    fromOption NoUpdateLog = 0x1++-- | Convert status code+toCode :: Int -> Code+toCode 0 = Success+toCode 1 = InvalidOperation+toCode 2 = HostNotFound+toCode 3 = ConnectionRefused+toCode 4 = SendError+toCode 5 = ReceiveError+toCode 6 = ExistingRecord+toCode 7 = NoRecordFound+toCode 9999 = MiscellaneousError+toCode _ = error "Invalid Code"++-- | TokyoTyrant Original Binary Protocal magic id.+--+-- Example:+--+-- >>> :m +Data.ByteString.Char8+-- >>> :set -XOverloadedStrings+-- >>> fromLBS (runPut $ putMagic 0x10) == "\xC8\x10"+-- True+--+putMagic :: B.Word8 -> B.Put+putMagic magic = B.putWord8 0xC8 >> B.putWord8 magic++-- | Option+--+-- Example:+--+-- >>> :m +Data.ByteString.Char8+-- >>> :set -XOverloadedStrings+-- >>> fromLBS (runPut $ putOptions [RecordLocking]) == "\0\0\0\1"+-- True+-- >>> fromLBS (runPut $ putOptions [GlobalLocking]) == "\0\0\0\2"+-- True+-- >>> fromLBS (runPut $ putOptions [RecordLocking, GlobalLocking]) == "\0\0\0\3"+-- True+--+putOptions :: BitFlag32 option =>+              [option]+           -> B.Put+putOptions = putWord32be . fromIntegral .+             foldl (.|.) 0 . map fromOption++-- | Get Length+--+-- Example:+--+-- >>> :m +Data.ByteString.Char8+-- >>> :set -XOverloadedStrings+-- >>> lengthBS32 "test"+-- 4+-- >>> lengthBS32 ""+-- 0+--+lengthBS32 :: BS.ByteString -> B.Word32+lengthBS32 = fromIntegral . BS.length++-- | Get Length+--+-- Example:+--+-- >>> :m +Data.ByteString.Lazy.Char8+-- >>> :set -XOverloadedStrings+-- >>> lengthLBS32 "test"+-- 4+-- >>> lengthLBS32 ""+-- 0+--+lengthLBS32 :: LBS.ByteString -> B.Word32+lengthLBS32 = fromIntegral . LBS.length++-- | Convert+--+-- Example:+--+-- >>> :m +Data.ByteString.Lazy.Char8+-- >>> :set -XOverloadedStrings+-- >>> fromLBS "test"+-- "test"+--+fromLBS :: LBS.ByteString -> BS.ByteString+fromLBS = BS.pack . LBS.unpack++-- | Send request.+yieldRequest :: ( MonadBaseControl IO m+                , MonadIO m ) =>+                B.Put+             -> MonarchT m ()+yieldRequest = sendLBS . runPut++-- | Receive response code.+responseCode :: ( MonadBaseControl IO m+                , MonadIO m ) =>+                MonarchT m Code+responseCode = toCode . fromIntegral . runGet B.getWord8 <$> recvLBS 1++-- | Parse byte string (lazy) value.+parseLBS :: ( MonadBaseControl IO m+            , MonadIO m ) =>+            MonarchT m LBS.ByteString+parseLBS = recvLBS 4 >>=+           recvLBS . fromIntegral . runGet getWord32be++-- | Parse byte string (strict) value.+parseBS :: ( MonadBaseControl IO m+           , MonadIO m ) =>+           MonarchT m BS.ByteString+parseBS = fromLBS <$> parseLBS++-- | Parse Word32 value.+parseWord32 :: ( MonadBaseControl IO m+               , MonadIO m ) =>+               MonarchT m B.Word32+parseWord32 = runGet getWord32be <$> recvLBS 4++-- | Parse Int64 value.+parseInt64 :: ( MonadBaseControl IO m+              , MonadIO m ) =>+              MonarchT m Int64+parseInt64 = runGet (B.get :: B.Get Int64) <$> recvLBS 8++-- | Parse Double value.+parseDouble :: ( MonadBaseControl IO m+               , MonadIO m ) =>+               MonarchT m Double+parseDouble = do+  integ <- fromIntegral <$> parseInt64+  fract <- fromIntegral <$> parseInt64+  return $ integ + fract * 1e-12++-- | Parse key value pair.+parseKeyValue :: ( MonadBaseControl IO m+                 , MonadIO m ) =>+                 MonarchT m (BS.ByteString, BS.ByteString)+parseKeyValue = do+  ksiz <- recvLBS 4+  vsiz <- recvLBS 4+  key <- recvLBS . fromIntegral $+         runGet getWord32be ksiz+  value <- recvLBS . fromIntegral $+           runGet getWord32be vsiz+  return (fromLBS key, fromLBS value)++-- | Make a query.+communicate :: ( MonadBaseControl IO m+               , MonadIO m) =>+               B.Put+            -> (Code -> MonarchT m a)+            -> MonarchT m a+communicate makeRequest makeResponse =+    yieldRequest makeRequest >>+    responseCode >>=+    makeResponse
+ test/Database/Monarch/ActionSpec.hs view
@@ -0,0 +1,287 @@+{-# LANGUAGE OverloadedStrings #-}+module Database.Monarch.ActionSpec ( spec ) where++import Control.Applicative+import Control.Monad.IO.Class+import Data.List+import qualified Data.ByteString as BS+import Data.ByteString.Char8 ()+import Test.Hspec+import Database.Monarch++spec :: Spec+spec = do+  describe "put" $ do+    it "store a record" casePutRecord+    it "overwrite a record if same key exists" casePutOverwriteRecord+  describe "mput" $ do+    it "store records" caseMputRecords+  describe "putkeep" $ do+    it "store a new record" casePutKeepNewRecord+    it "has no effect if same key exists" casePutKeepNoEffect+  describe "putcat" $ do+    it "concatenate a value at the end of the existing record" casePutCatRecord+    it "store a new record if there is no corresponding record" casePutCatNewRecord+  describe "putshl" $ do+    it "concatenate a value at the end of the existing record and shift it to the left" casePutShlRecord+    it "store a new record if there is no corresponding record" casePutShlNewRecord+  describe "putnr" $ do+    it "store a record" casePutNrRecord+    it "overwrite a record if same key exists" casePutNrOverwriteRecord+  describe "out" $ do+    it "remove a record" caseOutRecord+    it "no effect if same key not exists" caseOutNoEffect+  describe "mout" $ do+    it "remove records" caseMoutRecords+  describe "get" $ do+    it "retrieve a record" caseGetRecord+    it "retrieve large record" caseGetLargeRecord+  describe "mget" $ do+    it "retrieve records" caseMgetRecords+  describe "vsiz" $ do+    it "get the size of the value of a record" caseVsizRecord+  describe "iterinit" $ do+    it "initialize the iterator" caseIterinit+  describe "iternext" $ do+    it "get the next key of the iterator" caseIternext+    it "invalid if end iterator" caseIternextInvalid+  describe "fwmkeys" $ do+    it "get forward matching keys" caseFwmkeys++returns :: (Eq a, Show a) =>+           MonarchT IO a+        -> Either Code a+        -> IO ()+action `returns` expected = connTest >> poolTest+  where+  connTest = do result <- withMonarchConn "127.0.0.1" 1978 $ runMonarchConn $ do+                            vanish+                            result <- action+                            vanish+                            return result+                result `shouldBe` expected+  poolTest = do result <- withMonarchPool "127.0.0.1" 1978 20 $ runMonarchPool $ do+                            vanish+                            result <- action+                            vanish+                            return result+                result `shouldBe` expected++casePutRecord :: IO ()+casePutRecord =+    action `returns` Right (Just "bar")+    where+      action = do+        put "foo" "bar"+        get "foo"++casePutOverwriteRecord :: IO ()+casePutOverwriteRecord =+    action `returns` Right (Just "hoge")+    where+      action = do+        put "foo" "bar"+        put "foo" "hoge"+        get "foo"++caseMputRecords :: IO ()+caseMputRecords =+    action `returns` Right (Just "bob", Just "bar")+    where+      action = do+        multiplePut [("foo","bar"),("alice","bob")]+        bob <- get "alice"+        bar <- get "foo"+        return (bob, bar)++casePutKeepNewRecord :: IO ()+casePutKeepNewRecord =+    action `returns` Right (Just "hoge")+    where+      action = do+        putKeep "foo" "hoge"+        get "foo"++casePutKeepNoEffect :: IO ()+casePutKeepNoEffect =+    action `returns` Right (Just "bar")+    where+      action = do+        putKeep "foo" "bar"+        putKeep "foo" "hoge"+        get "foo"++casePutCatRecord :: IO ()+casePutCatRecord =+    action `returns` Right (Just "abracadabra")+    where+      action = do+        put "foo" "abra"+        putCat "foo" "cadabra"+        get "foo"++casePutCatNewRecord :: IO ()+casePutCatNewRecord =+    action `returns` Right (Just "cadabra")+    where+      action = do+        putCat "foo" "cadabra"+        get "foo"++casePutShlRecord :: IO ()+casePutShlRecord =+    action `returns` Right (Just "racadabra")+    where+      action = do+        put "foo" "abra"+        putShiftLeft "foo" "cadabra" 9+        get "foo"++casePutShlNewRecord :: IO ()+casePutShlNewRecord =+    action `returns` Right (Just "cadabra")+    where+      action = do+        putShiftLeft "foo" "cadabra" 4+        get "foo"++casePutNrRecord :: IO ()+casePutNrRecord =+    action `returns` Right (Just "bar")+    where+      action = do+        putNoResponse "foo" "bar"+        get "foo"++casePutNrOverwriteRecord :: IO ()+casePutNrOverwriteRecord =+    action `returns` Right (Just "hoge")+    where+      action = do+        putNoResponse "foo" "bar"+        putNoResponse "foo" "hoge"+        get "foo"++caseOutRecord :: IO ()+caseOutRecord =+    action `returns` Right (Just "bar", Nothing)+    where+      action = do+        put "foo" "bar"+        put "hoge" "fuga"+        out "hoge"+        stored <- get "foo"+        unstored <- get "hoge"+        return (stored, unstored)++caseOutNoEffect :: IO ()+caseOutNoEffect =+    action `returns` Right ()+    where+      action = do+        out "hoge"++caseMoutRecords :: IO ()+caseMoutRecords =+    action `returns` Right (Nothing, Nothing)+    where+      action = do+        put "foo" "bar"+        put "hoge" "fuga"+        multipleOut ["foo", "hoge"]+        bar <- get "foo"+        fuga <- get "hoge"+        return (bar, fuga)++caseGetRecord :: IO ()+caseGetRecord =+    action `returns` Right (Just "bar", Nothing)+    where+      action = do+        put "foo" "bar"+        stored <- get "foo"+        unstored <- get "bar"+        return (stored, unstored)++caseGetLargeRecord :: IO ()+caseGetLargeRecord = do+  content <- BS.concat . replicate 1024 <$> liftIO (BS.readFile "monarch.cabal")+  action content `returns` Right (Just content)+    where+      action content = do+        put "foo" content+        get "foo"++caseMgetRecords :: IO ()+caseMgetRecords =+    action `returns` Right [ ("foo", "bar")+                           , ("huga", "hoge")+                           , ("abra", "cadabra")+                           ]+    where+      action = do+        put "foo" "bar"+        put "huga" "hoge"+        put "abra" "cadabra"+        multipleGet [ "foo"+                    , "huga"+                    , "unstored"+                    , "abra"+                    ]++caseVsizRecord :: IO ()+caseVsizRecord =+    action `returns` Right (Just 3, Nothing)+    where+      action = do+        put "foo" "bar"+        stored <- valueSize "foo"+        unstored <- valueSize "bar"+        return (stored, unstored)++caseIterinit :: IO ()+caseIterinit =+    action `returns` Right True+    where+      action = do+        put "foo" "bar"+        put "fuga" "hoge"+        put "abra" "cadabra"+        iterInit+        key1 <- iterNext+        _ <- iterNext+        iterInit+        key2 <- iterNext+        return $ key1 == key2++caseIternext :: IO ()+caseIternext =+    action `returns` Right [ Just "abra", Just "foo", Just "fuga" ]+    where+      action = do+        put "foo" "bar"+        put "fuga" "hoge"+        put "abra" "cadabra"+        iterInit+        key1 <- iterNext+        key2 <- iterNext+        key3 <- iterNext+        return $ sort [key1, key2, key3]++caseIternextInvalid :: IO ()+caseIternextInvalid =+    action `returns` Right Nothing+    where+      action = do+        iterNext++caseFwmkeys :: IO ()+caseFwmkeys =+    action `returns` Right [ "abra", "abrac" ]+    where+      action = do+        put "abr" "acadabra"+        put "abra" "cadabra"+        put "abrac" "adabra"+        put "abraca" "dabra"+        sort <$> forwardMatchingKeys "abra" (Just 2)
+ test/Database/Monarch/Mock/ActionSpec.hs view
@@ -0,0 +1,285 @@+{-# LANGUAGE OverloadedStrings #-}+module Database.Monarch.Mock.ActionSpec ( spec ) where++import Control.Applicative+import Control.Monad.IO.Class+import Data.List+import qualified Data.ByteString as BS+import Data.ByteString.Char8 ()+import Test.Hspec+import Database.Monarch+import Database.Monarch.Mock++spec :: Spec+spec = do+  describe "put" $ do+    it "store a record" casePutRecord+    it "overwrite a record if same key exists" casePutOverwriteRecord+  describe "mput" $ do+    it "store records" caseMputRecords+  describe "putkeep" $ do+    it "store a new record" casePutKeepNewRecord+    it "has no effect if same key exists" casePutKeepNoEffect+  describe "putcat" $ do+    it "concatenate a value at the end of the existing record" casePutCatRecord+    it "store a new record if there is no corresponding record" casePutCatNewRecord+  describe "putshl" $ do+    it "concatenate a value at the end of the existing record and shift it to the left" casePutShlRecord+    it "store a new record if there is no corresponding record" casePutShlNewRecord+  describe "putnr" $ do+    it "store a record" casePutNrRecord+    it "overwrite a record if same key exists" casePutNrOverwriteRecord+  describe "out" $ do+    it "remove a record" caseOutRecord+    it "no effect if same key not exists" caseOutNoEffect+  describe "mout" $ do+    it "remove records" caseMoutRecords+  describe "get" $ do+    it "retrieve a record" caseGetRecord+    it "retrieve large record" caseGetLargeRecord+  describe "mget" $ do+    it "retrieve records" caseMgetRecords+  describe "vsiz" $ do+    it "get the size of the value of a record" caseVsizRecord+  -- describe "iterinit" $ do+  --   it "initialize the iterator" caseIterinit+  -- describe "iternext" $ do+  --   it "get the next key of the iterator" caseIternext+  --   it "invalid if end iterator" caseIternextInvalid+  describe "fwmkeys" $ do+    it "get forward matching keys" caseFwmkeys++returns :: (Eq a, Show a) =>+           MockT IO a+        -> Either Code a+        -> IO ()+action `returns` expected = mockTest+  where+  mockTest = do+    mdb <- newMockDB+    result <- runMock (do+                        vanish+                        result <- action+                        vanish+                        return result+                      ) mdb+    result `shouldBe` expected++casePutRecord :: IO ()+casePutRecord =+    action `returns` Right (Just "bar")+    where+      action = do+        put "foo" "bar"+        get "foo"++casePutOverwriteRecord :: IO ()+casePutOverwriteRecord =+    action `returns` Right (Just "hoge")+    where+      action = do+        put "foo" "bar"+        put "foo" "hoge"+        get "foo"++caseMputRecords :: IO ()+caseMputRecords =+    action `returns` Right (Just "bob", Just "bar")+    where+      action = do+        multiplePut [("foo","bar"),("alice","bob")]+        bob <- get "alice"+        bar <- get "foo"+        return (bob, bar)++casePutKeepNewRecord :: IO ()+casePutKeepNewRecord =+    action `returns` Right (Just "hoge")+    where+      action = do+        putKeep "foo" "hoge"+        get "foo"++casePutKeepNoEffect :: IO ()+casePutKeepNoEffect =+    action `returns` Right (Just "bar")+    where+      action = do+        putKeep "foo" "bar"+        putKeep "foo" "hoge"+        get "foo"++casePutCatRecord :: IO ()+casePutCatRecord =+    action `returns` Right (Just "abracadabra")+    where+      action = do+        put "foo" "abra"+        putCat "foo" "cadabra"+        get "foo"++casePutCatNewRecord :: IO ()+casePutCatNewRecord =+    action `returns` Right (Just "cadabra")+    where+      action = do+        putCat "foo" "cadabra"+        get "foo"++casePutShlRecord :: IO ()+casePutShlRecord =+    action `returns` Right (Just "racadabra")+    where+      action = do+        put "foo" "abra"+        putShiftLeft "foo" "cadabra" 9+        get "foo"++casePutShlNewRecord :: IO ()+casePutShlNewRecord =+    action `returns` Right (Just "cadabra")+    where+      action = do+        putShiftLeft "foo" "cadabra" 4+        get "foo"++casePutNrRecord :: IO ()+casePutNrRecord =+    action `returns` Right (Just "bar")+    where+      action = do+        putNoResponse "foo" "bar"+        get "foo"++casePutNrOverwriteRecord :: IO ()+casePutNrOverwriteRecord =+    action `returns` Right (Just "hoge")+    where+      action = do+        putNoResponse "foo" "bar"+        putNoResponse "foo" "hoge"+        get "foo"++caseOutRecord :: IO ()+caseOutRecord =+    action `returns` Right (Just "bar", Nothing)+    where+      action = do+        put "foo" "bar"+        put "hoge" "fuga"+        out "hoge"+        stored <- get "foo"+        unstored <- get "hoge"+        return (stored, unstored)++caseOutNoEffect :: IO ()+caseOutNoEffect =+    action `returns` Right ()+    where+      action = do+        out "hoge"++caseMoutRecords :: IO ()+caseMoutRecords =+    action `returns` Right (Nothing, Nothing)+    where+      action = do+        put "foo" "bar"+        put "hoge" "fuga"+        multipleOut ["foo", "hoge"]+        bar <- get "foo"+        fuga <- get "hoge"+        return (bar, fuga)++caseGetRecord :: IO ()+caseGetRecord =+    action `returns` Right (Just "bar", Nothing)+    where+      action = do+        put "foo" "bar"+        stored <- get "foo"+        unstored <- get "bar"+        return (stored, unstored)++caseGetLargeRecord :: IO ()+caseGetLargeRecord = do+  content <- BS.concat . replicate 1024 <$> liftIO (BS.readFile "monarch.cabal")+  action content `returns` Right (Just content)+    where+      action content = do+        put "foo" content+        get "foo"++caseMgetRecords :: IO ()+caseMgetRecords =+    action `returns` Right [ ("foo", "bar")+                           , ("huga", "hoge")+                           , ("abra", "cadabra")+                           ]+    where+      action = do+        put "foo" "bar"+        put "huga" "hoge"+        put "abra" "cadabra"+        multipleGet [ "foo"+                    , "huga"+                    , "unstored"+                    , "abra"+                    ]++caseVsizRecord :: IO ()+caseVsizRecord =+    action `returns` Right (Just 3, Nothing)+    where+      action = do+        put "foo" "bar"+        stored <- valueSize "foo"+        unstored <- valueSize "bar"+        return (stored, unstored)++_caseIterinit :: IO ()+_caseIterinit =+    action `returns` Right True+    where+      action = do+        put "foo" "bar"+        put "fuga" "hoge"+        put "abra" "cadabra"+        iterInit+        key1 <- iterNext+        _ <- iterNext+        iterInit+        key2 <- iterNext+        return $ key1 == key2++_caseIternext :: IO ()+_caseIternext =+    action `returns` Right [ Just "abra", Just "foo", Just "fuga" ]+    where+      action = do+        put "foo" "bar"+        put "fuga" "hoge"+        put "abra" "cadabra"+        iterInit+        key1 <- iterNext+        key2 <- iterNext+        key3 <- iterNext+        return $ sort [key1, key2, key3]++_caseIternextInvalid :: IO ()+_caseIternextInvalid =+    action `returns` Right Nothing+    where+      action = do+        iterNext++caseFwmkeys :: IO ()+caseFwmkeys =+    action `returns` Right [ "abra", "abrac" ]+    where+      action = do+        put "abr" "acadabra"+        put "abra" "cadabra"+        put "abrac" "adabra"+        put "abraca" "dabra"+        sort <$> forwardMatchingKeys "abra" (Just 2)
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
test/benchmark.hs view
@@ -21,25 +21,25 @@  ffi :: IO () ffi = do-  Right conn <- FFI.open "localhost" 1978+  Right conn <- FFI.open "127.0.0.1" 1978   FFI.put conn "foo" "bar"   FFI.close conn  mffi :: IO () mffi = do-  Right conn <- FFI.open "localhost" 1978+  Right conn <- FFI.open "127.0.0.1" 1978   FFI.mput conn $ replicate size ("foo", "bar")   FFI.close conn  monarch :: IO () monarch = do-  Right () <- Monarch.withMonarchConn "localhost" 1978 $+  Right () <- Monarch.withMonarchConn "127.0.0.1" 1978 $               Monarch.runMonarchConn $               Monarch.put "foo" "bar"   return ()  mmonarch :: IO () mmonarch = do-  Right () <- Monarch.withMonarchConn "localhost" 1978 $ Monarch.runMonarchConn $+  Right () <- Monarch.withMonarchConn "127.0.0.1" 1978 $ Monarch.runMonarchConn $          Monarch.multiplePut $ replicate size ("foo", "bar")   return ()
test/doctests.hs view
@@ -1,6 +1,7 @@ import Test.DocTest  main :: IO ()-main = doctest [ "-Lcabal-dev/lib"-               , "-package-conf=cabal-dev/packages-7.4.2.conf"-               , "Database/Monarch/Utils.hs" ]+main = doctest [ "-isrc"+               , "-ignore-package", "monads-tf"+               , "Database.Monarch.Utils"+               ]
− test/specs.hs
@@ -1,288 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-import Database.Monarch--import Control.Applicative-import Control.Monad.IO.Class-import Data.List-import qualified Data.ByteString as BS-import Data.ByteString.Char8 ()-import Test.HUnit-import Test.Hspec.Monadic-import Test.Hspec.HUnit ()--main :: IO ()-main = hspec $ do-         describe "put" $ do-           it "store a record" casePutRecord-           it "overwrite a record if same key exists" casePutOverwriteRecord-         describe "mput" $ do-           it "store records" caseMputRecords-         describe "putkeep" $ do-           it "store a new record" casePutKeepNewRecord-           it "has no effect if same key exists" casePutKeepNoEffect-         describe "putcat" $ do-           it "concatenate a value at the end of the existing record" casePutCatRecord-           it "store a new record if there is no corresponding record" casePutCatNewRecord-         describe "putshl" $ do-           it "concatenate a value at the end of the existing record and shift it to the left" casePutShlRecord-           it "store a new record if there is no corresponding record" casePutShlNewRecord-         describe "putnr" $ do-           it "store a record" casePutNrRecord-           it "overwrite a record if same key exists" casePutNrOverwriteRecord-         describe "out" $ do-           it "remove a record" caseOutRecord-           it "no effect if same key not exists" caseOutNoEffect-         describe "mout" $ do-           it "remove records" caseMoutRecords-         describe "get" $ do-           it "retrieve a record" caseGetRecord-           it "retrieve large record" caseGetLargeRecord-         describe "mget" $ do-           it "retrieve records" caseMgetRecords-         describe "vsiz" $ do-           it "get the size of the value of a record" caseVsizRecord-         describe "iterinit" $ do-           it "initialize the iterator" caseIterinit-         describe "iternext" $ do-           it "get the next key of the iterator" caseIternext-           it "invalid if end iterator" caseIternextInvalid-         describe "fwmkeys" $ do-           it "get forward matching keys" caseFwmkeys--returns :: (Eq a, Show a) =>-           MonarchT IO a-        -> Either Code a-        -> IO ()-action `returns` expected = connTest >> poolTest-  where-  connTest = do result <- withMonarchConn "localhost" 1978 $ runMonarchConn $ do-                            vanish-                            result <- action-                            vanish-                            return result-                result @?= expected-  poolTest = do result <- withMonarchPool "localhost" 1978 20 $ runMonarchPool $ do-                            vanish-                            result <- action-                            vanish-                            return result-                result @?= expected--casePutRecord :: Assertion-casePutRecord =-    action `returns` Right (Just "bar")-    where-      action = do-        put "foo" "bar"-        get "foo"--casePutOverwriteRecord :: Assertion-casePutOverwriteRecord =-    action `returns` Right (Just "hoge")-    where-      action = do-        put "foo" "bar"-        put "foo" "hoge"-        get "foo"--caseMputRecords :: Assertion-caseMputRecords =-    action `returns` Right (Just "bob", Just "bar")-    where-      action = do-        multiplePut [("foo","bar"),("alice","bob")]-        bob <- get "alice"-        bar <- get "foo"-        return (bob, bar)--casePutKeepNewRecord :: Assertion-casePutKeepNewRecord =-    action `returns` Right (Just "hoge")-    where-      action = do-        putKeep "foo" "hoge"-        get "foo"--casePutKeepNoEffect :: Assertion-casePutKeepNoEffect =-    action `returns` Right (Just "bar")-    where-      action = do-        putKeep "foo" "bar"-        putKeep "foo" "hoge"-        get "foo"--casePutCatRecord :: Assertion-casePutCatRecord =-    action `returns` Right (Just "abracadabra")-    where-      action = do-        put "foo" "abra"-        putCat "foo" "cadabra"-        get "foo"--casePutCatNewRecord :: Assertion-casePutCatNewRecord =-    action `returns` Right (Just "cadabra")-    where-      action = do-        putCat "foo" "cadabra"-        get "foo"--casePutShlRecord :: Assertion-casePutShlRecord =-    action `returns` Right (Just "racadabra")-    where-      action = do-        put "foo" "abra"-        putShiftLeft "foo" "cadabra" 9-        get "foo"--casePutShlNewRecord :: Assertion-casePutShlNewRecord =-    action `returns` Right (Just "cadabra")-    where-      action = do-        putShiftLeft "foo" "cadabra" 4-        get "foo"--casePutNrRecord :: Assertion-casePutNrRecord =-    action `returns` Right (Just "bar")-    where-      action = do-        putNoResponse "foo" "bar"-        get "foo"--casePutNrOverwriteRecord :: Assertion-casePutNrOverwriteRecord =-    action `returns` Right (Just "hoge")-    where-      action = do-        putNoResponse "foo" "bar"-        putNoResponse "foo" "hoge"-        get "foo"--caseOutRecord :: Assertion-caseOutRecord =-    action `returns` Right (Just "bar", Nothing)-    where-      action = do-        put "foo" "bar"-        put "hoge" "fuga"-        out "hoge"-        stored <- get "foo"-        unstored <- get "hoge"-        return (stored, unstored)--caseOutNoEffect :: Assertion-caseOutNoEffect =-    action `returns` Right ()-    where-      action = do-        out "hoge"--caseMoutRecords :: Assertion-caseMoutRecords =-    action `returns` Right (Nothing, Nothing)-    where-      action = do-        put "foo" "bar"-        put "hoge" "fuga"-        multipleOut ["foo", "hoge"]-        bar <- get "foo"-        fuga <- get "hoge"-        return (bar, fuga)--caseGetRecord :: Assertion-caseGetRecord =-    action `returns` Right (Just "bar", Nothing)-    where-      action = do-        put "foo" "bar"-        stored <- get "foo"-        unstored <- get "bar"-        return (stored, unstored)--caseGetLargeRecord :: Assertion-caseGetLargeRecord = do-  content <- BS.concat . replicate 1024 <$> liftIO (BS.readFile "test/specs.hs")-  action content `returns` Right (Just content)-    where-      action content = do-        put "foo" content-        get "foo"--caseMgetRecords :: Assertion-caseMgetRecords =-    action `returns` Right [ ("foo", "bar")-                           , ("huga", "hoge")-                           , ("abra", "cadabra")-                           ]-    where-      action = do-        put "foo" "bar"-        put "huga" "hoge"-        put "abra" "cadabra"-        multipleGet [ "foo"-                    , "huga"-                    , "unstored"-                    , "abra"-                    ]--caseVsizRecord :: Assertion-caseVsizRecord =-    action `returns` Right (Just 3, Nothing)-    where-      action = do-        put "foo" "bar"-        stored <- valueSize "foo"-        unstored <- valueSize "bar"-        return (stored, unstored)--caseIterinit :: Assertion-caseIterinit =-    action `returns` Right True-    where-      action = do-        put "foo" "bar"-        put "fuga" "hoge"-        put "abra" "cadabra"-        iterInit-        key1 <- iterNext-        _ <- iterNext-        iterInit-        key2 <- iterNext-        return $ key1 == key2--caseIternext :: Assertion-caseIternext =-    action `returns` Right [ Just "abra", Just "foo", Just "fuga" ]-    where-      action = do-        put "foo" "bar"-        put "fuga" "hoge"-        put "abra" "cadabra"-        iterInit-        key1 <- iterNext-        key2 <- iterNext-        key3 <- iterNext-        return $ sort [key1, key2, key3]--caseIternextInvalid :: Assertion-caseIternextInvalid =-    action `returns` Right Nothing-    where-      action = do-        iterNext--caseFwmkeys :: Assertion-caseFwmkeys =-    action `returns` Right [ "abra", "abrac" ]-    where-      action = do-        put "abr" "acadabra"-        put "abra" "cadabra"-        put "abrac" "adabra"-        put "abraca" "dabra"-        sort <$> forwardMatchingKeys "abra" (Just 2)