MonadInvertIO

This commit is contained in:
Michael Snoyman 2010-10-15 11:50:04 +02:00
parent 2f61ef6d39
commit ab4c7e3ae2
5 changed files with 49 additions and 42 deletions

View File

@ -1,5 +1,4 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE PackageImports #-}
module Yesod module Yesod
( module Yesod.Request ( module Yesod.Request
, module Yesod.Content , module Yesod.Content
@ -13,7 +12,7 @@ module Yesod
, Application , Application
, lift , lift
, liftIO , liftIO
, MonadCatchIO , MonadInvertIO
, mempty , mempty
) where ) where
@ -36,7 +35,7 @@ import Yesod.Form
import Yesod.Widget import Yesod.Widget
import Network.Wai (Application) import Network.Wai (Application)
import Yesod.Hamlet import Yesod.Hamlet
import "transformers" Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Class (lift)
import "transformers" Control.Monad.IO.Class (liftIO) import Control.Monad.IO.Class (liftIO)
import "MonadCatchIO-transformers" Control.Monad.CatchIO (MonadCatchIO)
import Data.Monoid (mempty) import Data.Monoid (mempty)
import Control.Monad.Invert (MonadInvertIO)

View File

@ -73,7 +73,7 @@ module Yesod.Handler
, YesodApp (..) , YesodApp (..)
, toMasterHandler , toMasterHandler
, localNoCurrent , localNoCurrent
, finallyHandler , HandlerData
#if TEST #if TEST
, testSuite , testSuite
#endif #endif
@ -94,17 +94,18 @@ import Control.Monad.IO.Class
import Control.Monad.Trans.Class import Control.Monad.Trans.Class
import Control.Monad.Trans.Writer import Control.Monad.Trans.Writer
import Control.Monad.Trans.Reader import Control.Monad.Trans.Reader
import "MonadCatchIO-transformers" Control.Monad.CatchIO (MonadCatchIO)
import qualified "MonadCatchIO-transformers" Control.Monad.CatchIO as C
import System.IO import System.IO
import qualified Network.Wai as W import qualified Network.Wai as W
import Control.Monad.Attempt import Control.Failure (Failure (failure))
import Data.ByteString.UTF8 (toString) import Data.ByteString.UTF8 (toString)
import qualified Data.ByteString.Lazy.UTF8 as L import qualified Data.ByteString.Lazy.UTF8 as L
import Text.Hamlet import Text.Hamlet
import Control.Monad.Invert (MonadInvertIO (..))
import Control.Monad (liftM)
#if TEST #if TEST
import Test.Framework (testGroup, Test) import Test.Framework (testGroup, Test)
import Test.Framework.Providers.HUnit (testCase) import Test.Framework.Providers.HUnit (testCase)
@ -153,15 +154,28 @@ toMasterHandler tm ts route (GHandler h) =
-- 'WriterT' for headers and session, and an 'MEitherT' monad for handling -- 'WriterT' for headers and session, and an 'MEitherT' monad for handling
-- special responses. It is declared as a newtype to make compiler errors more -- special responses. It is declared as a newtype to make compiler errors more
-- readable. -- readable.
newtype GHandler sub master a = GHandler { unGHandler :: newtype GHandler sub master a =
ReaderT (HandlerData sub master) ( GHandler
{ unGHandler :: GHInner sub master a
}
deriving (Functor, Applicative, Monad, MonadIO)
type GHInner s m =
ReaderT (HandlerData s m) (
MEitherT HandlerContents ( MEitherT HandlerContents (
WriterT (Endo [Header]) ( WriterT (Endo [Header]) (
WriterT (Endo [(String, Maybe String)]) ( WriterT (Endo [(String, Maybe String)]) (
IO IO
)))) a ))))
}
deriving (Functor, Applicative, Monad, MonadIO, MonadCatchIO) instance MonadInvertIO (GHandler s m) where
newtype InvertedIO (GHandler s m) a =
InvGHandlerIO
{ runInvGHandlerIO :: InvertedIO (GHInner s m) a
}
type InvertedArg (GHandler s m) = (HandlerData s m, ())
invertIO = liftM (fmap InvGHandlerIO) . invertIO . unGHandler
revertIO f = GHandler $ revertIO $ liftM runInvGHandlerIO . f
type Endo a = a -> a type Endo a = a -> a
@ -475,24 +489,7 @@ localNoCurrent =
testSuite :: Test testSuite :: Test
testSuite = testGroup "Yesod.Handler" testSuite = testGroup "Yesod.Handler"
[ testCase "finally" caseFinally [
] ]
caseFinally :: Assertion
caseFinally = do
i <- newIORef (1 :: Int)
let h = finallyHandler (do
liftIO $ writeIORef i 2
() <- redirectString RedirectTemporary ""
return ()) $ liftIO $ writeIORef i 3
let y = runHandler h undefined undefined undefined undefined undefined
_ <- unYesodApp y undefined undefined undefined
j <- readIORef i
j @?= 3
#endif #endif
-- | A version of 'finally' which works correctly with short-circuiting.
finallyHandler :: GHandler s m a -> GHandler s m b -> GHandler s m a
finallyHandler (GHandler (ReaderT thing)) (GHandler (ReaderT after)) =
GHandler $ ReaderT $ \hd -> mapMEitherT (`C.finally` runMEitherT (after hd)) (thing hd)

View File

@ -33,17 +33,21 @@ import Control.Monad.Trans.State
import Text.Hamlet import Text.Hamlet
import Text.Cassius import Text.Cassius
import Text.Julius import Text.Julius
import Yesod.Handler (Route, GHandler) import Yesod.Handler (Route, GHandler, HandlerData)
import Control.Applicative (Applicative) import Control.Applicative (Applicative)
import Control.Monad.IO.Class (MonadIO) import Control.Monad.IO.Class (MonadIO)
import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Class (lift)
import "MonadCatchIO-transformers" Control.Monad.CatchIO (MonadCatchIO)
import Yesod.Internal import Yesod.Internal
import Control.Monad.Invert (MonadInvertIO (..))
import Control.Monad (liftM)
-- | A generic widget, allowing specification of both the subsite and master -- | A generic widget, allowing specification of both the subsite and master
-- site datatypes. This is basically a large 'WriterT' stack keeping track of -- site datatypes. This is basically a large 'WriterT' stack keeping track of
-- dependencies along with a 'StateT' to track unique identifiers. -- dependencies along with a 'StateT' to track unique identifiers.
newtype GWidget sub master a = GWidget ( newtype GWidget s m a = GWidget { unGWidget :: GWInner s m a }
deriving (Functor, Applicative, Monad, MonadIO)
type GWInner sub master =
WriterT (Body (Route master)) ( WriterT (Body (Route master)) (
WriterT (Last Title) ( WriterT (Last Title) (
WriterT (UniqueList (Script (Route master))) ( WriterT (UniqueList (Script (Route master))) (
@ -53,11 +57,19 @@ newtype GWidget sub master a = GWidget (
WriterT (Head (Route master)) ( WriterT (Head (Route master)) (
StateT Int ( StateT Int (
GHandler sub master GHandler sub master
)))))))) a) ))))))))
deriving (Functor, Applicative, Monad, MonadIO, MonadCatchIO)
instance Monoid (GWidget sub master ()) where instance Monoid (GWidget sub master ()) where
mempty = return () mempty = return ()
mappend x y = x >> y mappend x y = x >> y
instance MonadInvertIO (GWidget s m) where
newtype InvertedIO (GWidget s m) a =
InvGWidgetIO
{ runInvGWidgetIO :: InvertedIO (GWInner s m) a
}
type InvertedArg (GWidget s m) =
(Int, (HandlerData s m, ()))
invertIO = liftM (fmap InvGWidgetIO) . invertIO . unGWidget
revertIO f = GWidget $ revertIO $ liftM runInvGWidgetIO . f
instance HamletValue (GWidget s m ()) where instance HamletValue (GWidget s m ()) where
newtype HamletMonad (GWidget s m ()) a = newtype HamletMonad (GWidget s m ()) a =

View File

@ -52,7 +52,7 @@ import qualified Web.ClientSession as CS
import qualified Data.ByteString.UTF8 as BSU import qualified Data.ByteString.UTF8 as BSU
import Database.Persist import Database.Persist
import Control.Monad.Trans.Class (MonadTrans (..)) import Control.Monad.Trans.Class (MonadTrans (..))
import Control.Monad.Attempt (Failure) import Control.Failure (Failure)
import qualified Data.ByteString as S import qualified Data.ByteString as S
import qualified Network.Wai.Middleware.CleanPath import qualified Network.Wai.Middleware.CleanPath
import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy as L

View File

@ -37,13 +37,11 @@ library
, clientsession >= 0.4.0 && < 0.5 , clientsession >= 0.4.0 && < 0.5
, pureMD5 >= 1.1.0.0 && < 2.2 , pureMD5 >= 1.1.0.0 && < 2.2
, random >= 1.0.0.2 && < 1.1 , random >= 1.0.0.2 && < 1.1
, control-monad-attempt >= 0.3 && < 0.4
, cereal >= 0.2 && < 0.4 , cereal >= 0.2 && < 0.4
, dataenc >= 0.13.0.2 && < 0.14 , dataenc >= 0.13.0.2 && < 0.14
, old-locale >= 1.0.0.2 && < 1.1 , old-locale >= 1.0.0.2 && < 1.1
, persistent >= 0.2.2 && < 0.3 , persistent >= 0.3.0 && < 0.4
, neither >= 0.0.0 && < 0.1 , neither >= 0.1.0 && < 0.2
, MonadCatchIO-transformers >= 0.2.2.0 && < 0.3
, data-object >= 0.3.1 && < 0.4 , data-object >= 0.3.1 && < 0.4
, network >= 2.2.1.5 && < 2.3 , network >= 2.2.1.5 && < 2.3
, email-validate >= 0.2.5 && < 0.3 , email-validate >= 0.2.5 && < 0.3
@ -51,6 +49,7 @@ library
, web-routes >= 0.23 && < 0.24 , web-routes >= 0.23 && < 0.24
, xss-sanitize >= 0.2 && < 0.3 , xss-sanitize >= 0.2 && < 0.3
, data-default >= 0.2 && < 0.3 , data-default >= 0.2 && < 0.3
, failure >= 0.1 && < 0.2
exposed-modules: Yesod exposed-modules: Yesod
Yesod.Content Yesod.Content
Yesod.Dispatch Yesod.Dispatch