Skip to content
Snippets Groups Projects
Verified Commit 82102e70 authored by jan.hamal.dvorak's avatar jan.hamal.dvorak
Browse files

Add a simple cache to MonadAction

parent 0364018a
No related branches found
No related tags found
No related merge requests found
...@@ -83,6 +83,11 @@ module Hikaru.Action ...@@ -83,6 +83,11 @@ module Hikaru.Action
, getLanguages , getLanguages
, setLanguages , setLanguages
-- ** Cacheing
, withCache
, dropCache
, dropCaches
-- ** Finalizing -- ** Finalizing
, registerFinalizer , registerFinalizer
...@@ -100,6 +105,7 @@ where ...@@ -100,6 +105,7 @@ where
import qualified Data.ByteString.Lazy as LBS import qualified Data.ByteString.Lazy as LBS
import qualified Data.Text.Lazy as LT import qualified Data.Text.Lazy as LT
import qualified Network.Wai.Parse as Parse import qualified Network.Wai.Parse as Parse
import qualified Data.Map.Strict as Map
import Control.Monad.Trans import Control.Monad.Trans
import Control.Monad.Trans.Resource import Control.Monad.Trans.Resource
...@@ -108,30 +114,30 @@ where ...@@ -108,30 +114,30 @@ where
import Data.ByteString (ByteString, length) import Data.ByteString (ByteString, length)
import Data.String.Conversions import Data.String.Conversions
import Data.Text (Text) import Data.Text (Text)
import Hikaru.Media
import Hikaru.Types
import Lucid import Lucid
import Network.HTTP.Types.Header import Network.HTTP.Types.Header
import Network.HTTP.Types.Method import Network.HTTP.Types.Method
import Network.HTTP.Types.Status import Network.HTTP.Types.Status
import Network.Wai import Network.Wai
import Web.Cookie import Web.Cookie
import Hikaru.Media
import Hikaru.Types
-- | -- |
-- Inside the 'MonadAction' the original 'Request' is available and we can -- 'MonadAction' provides access to the original 'Request' and means to
-- query it for information while building a fresh 'Response' to send out. -- build a 'Response' to send out.
-- --
-- * Request header is always fully parsed. -- * Request headers are always fully parsed.
-- * Body is left untouched until you decide what to do with it. -- * Body is left untouched until you decide what to do with it.
-- --
-- Be careful not to blow up your memory usage by reading a multi-gigabyte -- Be careful not to blow out your memory usage by reading a multi-gigabyte
-- attachment into a strict ByteString or something. -- attachment into a strict ByteString or something similar.
-- --
class (MonadIO m) => MonadAction m where class (MonadIO m) => MonadAction m where
-- | -- |
-- Return the action environment, including the 'Request' object, cached -- Return the action environment, including the 'Request' object,
-- content from the user and the pending 'Response'. -- cached content from the user and the pending 'Response'.
-- --
getActionEnv :: m ActionEnv getActionEnv :: m ActionEnv
...@@ -188,6 +194,7 @@ where ...@@ -188,6 +194,7 @@ where
, aeBodyLimit :: IORef Int64 , aeBodyLimit :: IORef Int64
, aeBodyCounter :: IORef Int64 , aeBodyCounter :: IORef Int64
, aeLanguages :: IORef [Text] , aeLanguages :: IORef [Text]
, aeCache :: IORef (Map.Map Text Dynamic)
} }
...@@ -258,6 +265,7 @@ where ...@@ -258,6 +265,7 @@ where
<*> newIORef (10 * 1024 * 1024) <*> newIORef (10 * 1024 * 1024)
<*> newIORef 0 <*> newIORef 0
<*> newIORef [] <*> newIORef []
<*> newIORef Map.empty
-- Inspecting Request ------------------------------------------------------ -- Inspecting Request ------------------------------------------------------
...@@ -271,7 +279,7 @@ where ...@@ -271,7 +279,7 @@ where
-- | -- |
-- Obtain the request method, such as \"GET\" or \"POST\". -- Obtain the request method, such as @GET@ or @POST@.
-- --
getMethod :: (MonadAction m) => m Method getMethod :: (MonadAction m) => m Method
getMethod = requestMethod <$> getRequest getMethod = requestMethod <$> getRequest
...@@ -816,7 +824,7 @@ where ...@@ -816,7 +824,7 @@ where
-- values in a sensible way. -- values in a sensible way.
-- --
-- @ -- @
-- modifyHeader "Vary" $ maybe "Accept" (<> ", Accept") -- modifyHeader 'hVary' $ maybe "Accept" (<> ", Accept")
-- @ -- @
-- --
modifyHeader :: (MonadAction m) modifyHeader :: (MonadAction m)
...@@ -1025,6 +1033,54 @@ where ...@@ -1025,6 +1033,54 @@ where
setLanguages = setActionField aeLanguages setLanguages = setActionField aeLanguages
-- Cacheing ----------------------------------------------------------------
-- |
-- Run the effect only if it was not found in the cache.
--
-- The cache is request-specific and will be dropped after the request
-- has been handled. It can be also dropped manually using 'dropCache'
-- or 'dropCaches'.
--
-- The first time 'withCache' is called with a given key, the resulting
-- value is stored in the cache under that key. Next time, the effect is
-- not executed and the cached value is returned instead.
--
-- Since 'Dynamic' is used under the wraps, reusing the same key with a
-- different type of value is safe and will result in overwriting the
-- old key.
--
withCache :: (MonadAction m, Typeable a) => Text -> m a -> m a
withCache key makeValue = do
cache <- getActionField aeCache
case fromDynamic =<< Map.lookup key cache of
Nothing -> do
value <- makeValue
modifyActionField aeCache (Map.insert key (toDyn value))
return value
Just value -> do
return value
-- |
-- Drop a single cached value.
--
dropCache :: (MonadAction m) => Text -> m ()
dropCache key = do
modifyActionField aeCache (Map.delete key)
-- |
-- Drop all cached values.
--
dropCaches :: (MonadAction m) => m ()
dropCaches = do
modifyActionField aeCache (const Map.empty)
-- Finalizing -------------------------------------------------------------- -- Finalizing --------------------------------------------------------------
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment