packages feed

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 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)