{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} -- | A Snaplet for using the Riak database (via the package) -- Modelled on module Snap.Snaplet.Riak ( RiakDB , withRiak , riakInit , riakCreate , HasRiak(getRiakState) , get , getMany , modify , modify_ , delete , put , putMany , listBuckets , foldKeys , getBucket , setBucket , mapReduce ) where import Prelude hiding ((.)) import Control.Category ((.)) import Control.Monad.State (MonadState, gets) import Control.Monad.IO.Class import Data.Lens.Common import Data.Lens.Template import Snap.Snaplet import Network.Riak (Connection, Client, Resolvable) import Network.Riak.Types import qualified Network.Riak as R import Network.Riak.Protocol.BucketProps import Network.Riak.Protocol.MapReduce import Network.Riak.Connection.Pool import Data.Time.Clock import Data.Aeson.Types import Data.Sequence -- | Riak Snaplet state. Stores a connection pool shared between handlers. data RiakDB = RiakDB { _pool :: Pool } makeLens ''RiakDB -- | A class which, when implemented, allows the wrapper functions below to -- be used without explicitly managing the connection and having to use liftIO. -- The wrappers are pretty mechanically defined, so Template Haskell could -- probably be used instead- especially since there are so many options in -- Network.Riak class MonadIO m => HasRiak m where getRiakState :: m RiakDB -- | Perform an action using a Riak Connection in the Riak snaplet -- -- > result <- withRiak $ \c -> get c "myBucket" "myKey" Default withRiak :: (HasRiak m) => (Connection -> IO a) -> m a withRiak f = do c <- getRiakState liftIO $ withConnection (getL pool $ c) f -- | Utility function for creating the Riak snaplet from an Initializer makeRiak :: Initializer b v v -> SnapletInit b v makeRiak = makeSnaplet "snaplet-riak" "Riak Snaplet." Nothing -- | Initialize the Riak snaplet riakInit :: Pool -> SnapletInit b RiakDB riakInit pool = makeRiak . return $ RiakDB pool -- | Thin wrapper around 'Network.Riak.create' to run within 'SnapletInit' riakCreate :: Client -> Int -> NominalDiffTime -> Int -> SnapletInit b RiakDB riakCreate c ns t nc = makeRiak $ do pool <- liftIO $ create c ns t nc return $ RiakDB pool get :: (HasRiak m, FromJSON a, ToJSON a, Resolvable a) => Bucket -> Key -> R -> m (Maybe (a, VClock)) get b k r = withRiak $ \conn -> R.get conn b k r getMany :: (HasRiak m, FromJSON a, ToJSON a, Resolvable a) => Bucket -> [Key] -> R -> m [Maybe (a, VClock)] getMany b ks r = withRiak $ \conn -> R.getMany conn b ks r modify :: (HasRiak m, FromJSON a, ToJSON a, Resolvable a) => Bucket -> Key -> R -> W -> DW -> (Maybe a -> IO (a, b)) -> m (a, b) modify b k r w dw f = withRiak $ \conn -> R.modify conn b k r w dw f modify_ :: (HasRiak m, FromJSON a, ToJSON a, Resolvable a) => Bucket -> Key -> R -> W -> DW -> (Maybe a -> IO a) -> m a modify_ b k r w dw f = withRiak $ \conn -> R.modify_ conn b k r w dw f delete :: (HasRiak m) => Bucket -> Key -> RW -> m () delete b k rw = withRiak $ \conn -> R.delete conn b k rw put :: (HasRiak m, Eq a, FromJSON a, ToJSON a, Resolvable a) => Bucket -> Key -> Maybe VClock -> a -> W -> DW -> m (a, VClock) put b k vc c w dw = withRiak $ \conn -> R.put conn b k vc c w dw putMany :: (HasRiak m, Eq a, FromJSON a, ToJSON a, Resolvable a) => Bucket -> [(Key, Maybe VClock, a)] -> W -> DW -> m [(a, VClock)] putMany b ks w dw = withRiak $ \conn -> R.putMany conn b ks w dw listBuckets :: HasRiak m => m (Seq Bucket) listBuckets = withRiak R.listBuckets foldKeys :: HasRiak m => Bucket -> (a -> Key -> IO a) -> a -> m a foldKeys b f x = withRiak $ \conn -> R.foldKeys conn b f x getBucket :: HasRiak m => Bucket -> m BucketProps getBucket b = withRiak $ \conn -> R.getBucket conn b setBucket :: HasRiak m => Bucket -> BucketProps -> m () setBucket b props = withRiak $ \conn -> R.setBucket conn b props mapReduce :: HasRiak m => Job -> (a -> MapReduce -> a) -> a -> m a mapReduce job f z0 = withRiak $ \conn -> R.mapReduce conn job f z0