apiary-memcached (empty) → 0.17.0.0
raw patch · 4 files changed
+190/−0 lines, 4 filesdep +apiarydep +basedep +binarysetup-changed
Dependencies added: apiary, base, binary, data-default-class, memcached-binary, monad-control, text, transformers
Files
- LICENSE +20/−0
- Setup.hs +2/−0
- apiary-memcached.cabal +35/−0
- src/Web/Apiary/Memcached.hs +133/−0
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2014 Hirotomo Moriwaki++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ apiary-memcached.cabal view
@@ -0,0 +1,35 @@+name: apiary-memcached+version: 0.17.0.0+synopsis: memcached client for apiary web framework.+-- description:+license: MIT+license-file: LICENSE+author: HirotomoMoriwaki<philopon.dependence@gmail.com>+maintainer: HirotomoMoriwaki<philopon.dependence@gmail.com>+Homepage: https://github.com/philopon/apiary+Bug-reports: https://github.com/philopon/apiary/issues+copyright: (c) 2014 Hirotomo Moriwaki+category: Web+build-type: Simple+stability: experimental+-- extra-source-files: +cabal-version: >=1.10++library+ exposed-modules: Web.Apiary.Memcached+ other-modules: + build-depends: base >=4.6 && <4.8+ , apiary >=0.17 && <0.18+ , memcached-binary >=0.1 && <0.3+ , data-default-class >=0.0 && <0.1+ , binary >=0.7 && <0.8+ , text >=1.1 && <1.3+ , transformers >=0.3 && <0.5+ , monad-control >=0.3 && <0.4+ hs-source-dirs: src+ ghc-options: -O2 -Wall+ default-language: Haskell2010++source-repository head+ type: git+ location: git://github.com/philopon/apiary.git
+ src/Web/Apiary/Memcached.hs view
@@ -0,0 +1,133 @@+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}++module Web.Apiary.Memcached+ ( Memcached, CacheConfig(..), MemcachedConfig(..)+ -- * initializer+ , initMemcached, initHerokuMemcached++ -- * raw query+ , memcached++ -- * cache+ , cache, cacheMaybe+ ) where++import Web.Apiary+import Web.Apiary.Heroku++import Control.Applicative+import Control.Monad.Trans.Maybe+import Control.Monad.Trans.Control++import Data.Default.Class+import Data.Apiary.Extension+import Data.Apiary.Proxy+import qualified Data.Binary as B+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Data.Text.Read as T++import Database.Memcached.Binary.IO+import qualified Database.Memcached.Binary.Maybe as Maybe++data Memcached = Memcached Connection MemcachedConfig++data CacheConfig = CacheConfig+ { cacheFlags :: Key -> Flags+ , cacheExpiry :: Expiry+ , cacheNotHitExpiry :: Expiry+ }++instance Default CacheConfig where+ def = CacheConfig (\_ -> 0) 0 0++data MemcachedConfig = MemcachedConfig+ { connectInfo :: ConnectInfo+ , cacheConfig :: Maybe CacheConfig+ }++instance Default MemcachedConfig where+ def = MemcachedConfig def Nothing++initMemcached :: MonadBaseControl IO m => MemcachedConfig -> Initializer' m Memcached+initMemcached cfg = initializerBracket' $ \m -> control $ \run -> + withConnection (connectInfo cfg) (\c -> run $ m (Memcached c cfg))++getHerokuConfig :: T.Text -> MemcachedConfig -> Heroku -> MaybeT IO MemcachedConfig+getHerokuConfig pfx ci exts = do+ svr <- MaybeT $ getHerokuEnv' (pfx `T.append` "_SERVERS") exts+ usr <- liftIO $ getHerokuEnv' (pfx `T.append` "_USERNAME") exts+ pwd <- liftIO $ getHerokuEnv' (pfx `T.append` "_PASSWORD") exts++ let (hst, prtTxt) = T.breakOnEnd ":" svr+ prt <- either fail (return . fst) $ T.decimal prtTxt++ let auth = Plain <$> (T.encodeUtf8 <$> usr) <*> (T.encodeUtf8 <$> pwd)++ return ci {connectInfo = (connectInfo ci)+ { connectHost = T.unpack $ T.init hst+ , connectPort = PortNumber prt+ , connectAuth =+ maybe id (\a -> (a:)) auth $ connectAuth (connectInfo ci)+ }}++-- | initialize memcached extension using heroku service.+--+-- compatile:+--+-- * Memcachier+-- * Memcache cloud+--+initHerokuMemcached :: (Has Heroku exts, MonadBaseControl IO m)+ => MemcachedConfig -> Initializer m exts (Memcached ': exts)+initHerokuMemcached cfg = initializerBracket $ \exts m -> control $ \run -> do+ let hc = getExtension Proxy exts+ cfg' <- fmap (maybe cfg id) . runMaybeT $+ getHerokuConfig "MEMCACHIER" cfg hc <|>+ getHerokuConfig "MEMCACHEDCLOUD" cfg hc+ withConnection (connectInfo cfg') (\c -> run $ m (Memcached c cfg'))++memcached :: (Has Memcached exts, MonadIO m)+ => (Connection -> IO a) -> ActionT exts m a+memcached q = do+ Memcached conn _ <- getExt Proxy+ liftIO $ q conn++cache :: (MonadIO m, Has Memcached exts)+ => Key -> ActionT exts m Value -> ActionT exts m Value+cache key actn = do+ Memcached conn cfg <- getExt Proxy+ case cacheConfig cfg of+ Nothing -> actn+ Just cc -> liftIO (Maybe.get_ key conn) >>= \case+ Just cr -> return cr+ Nothing -> do+ ar <- actn+ liftIO $ set (cacheFlags cc key)+ (cacheExpiry cc) key ar conn+ return ar++cacheMaybe :: (MonadIO m, Has Memcached exts)+ => Key -> ActionT exts m (Maybe Value)+ -> ActionT exts m (Maybe Value)+cacheMaybe key actn = do+ Memcached conn cfg <- getExt Proxy+ case cacheConfig cfg of+ Nothing -> actn+ Just cc -> liftIO (Maybe.get_ key conn) >>= \case+ Just cr -> return $ B.decode cr+ Nothing -> actn >>= \case+ Nothing -> do+ liftIO $ set (cacheFlags cc key)+ (cacheNotHitExpiry cc) key (B.encode (Nothing :: Maybe Value)) conn+ return Nothing+ Just ar -> do+ liftIO $ set (cacheFlags cc key)+ (cacheExpiry cc) key (B.encode $ Just ar) conn+ return (Just ar)