GForm uses ReaderT

This commit is contained in:
Michael Snoyman 2010-08-16 14:48:38 +03:00
parent 2cefc3c2a7
commit bca6317490
4 changed files with 52 additions and 21 deletions

View File

@ -44,6 +44,7 @@ import Data.Maybe (fromMaybe, mapMaybe)
import "transformers" Control.Monad.IO.Class import "transformers" Control.Monad.IO.Class
import Control.Monad ((<=<)) import Control.Monad ((<=<))
import Control.Monad.Trans.State import Control.Monad.Trans.State
import Control.Monad.Trans.Reader
import Language.Haskell.TH.Syntax import Language.Haskell.TH.Syntax
import Database.Persist.Base (EntityDef (..)) import Database.Persist.Base (EntityDef (..))
import Data.Char (toUpper, isUpper) import Data.Char (toUpper, isUpper)
@ -76,7 +77,8 @@ runFormGeneric :: Env
-> FileEnv -> FileEnv
-> GForm sub y xml a -> GForm sub y xml a
-> GHandler sub y (FormResult a, xml, Enctype) -> GHandler sub y (FormResult a, xml, Enctype)
runFormGeneric env fe f = evalStateT (deform f env fe) $ IntSingle 1 runFormGeneric env fe (GForm f) =
runReaderT (runReaderT (evalStateT f $ IntSingle 1) env) fe
-- | Run a form against POST parameters. -- | Run a form against POST parameters.
runFormPost :: GForm sub y xml a runFormPost :: GForm sub y xml a

View File

@ -14,6 +14,9 @@ module Yesod.Form.Core
, fieldsToInput , fieldsToInput
, mapFormXml , mapFormXml
, checkForm , checkForm
, askParams
, askFiles
, liftForm
-- * Data types -- * Data types
, FieldInfo (..) , FieldInfo (..)
, FormFieldSettings (..) , FormFieldSettings (..)
@ -27,6 +30,8 @@ module Yesod.Form.Core
) where ) where
import Control.Monad.Trans.State import Control.Monad.Trans.State
import Control.Monad.Trans.Reader
import Control.Monad.Trans.Class (lift)
import Yesod.Handler import Yesod.Handler
import Yesod.Widget import Yesod.Widget
import Data.Monoid (Monoid (..)) import Data.Monoid (Monoid (..))
@ -85,7 +90,11 @@ incrInts (IntCons i is) = (i + 1) `IntCons` is
-- | A generic form, allowing you to specifying the subsite datatype, master -- | A generic form, allowing you to specifying the subsite datatype, master
-- site datatype, a datatype for the form XML and the return type. -- site datatype, a datatype for the form XML and the return type.
newtype GForm sub y xml a = GForm newtype GForm sub y xml a = GForm
{ deform :: Env -> FileEnv -> StateT Ints (GHandler sub y) (FormResult a, xml, Enctype) { deform :: StateT Ints (
ReaderT Env (
ReaderT FileEnv (
(GHandler sub y)
))) (FormResult a, xml, Enctype)
} }
type Env = [(String, String)] type Env = [(String, String)]
@ -112,23 +121,23 @@ shallowerFormIdent = do
instance Monoid xml => Functor (GForm sub url xml) where instance Monoid xml => Functor (GForm sub url xml) where
fmap f (GForm g) = fmap f (GForm g) =
GForm $ \env fe -> liftM (first3 $ fmap f) (g env fe) GForm $ liftM (first3 $ fmap f) g
where where
first3 f' (x, y, z) = (f' x, y, z) first3 f' (x, y, z) = (f' x, y, z)
instance Monoid xml => Applicative (GForm sub url xml) where instance Monoid xml => Applicative (GForm sub url xml) where
pure a = GForm $ const $ const $ return (pure a, mempty, mempty) pure a = GForm $ return (pure a, mempty, mempty)
(GForm f) <*> (GForm g) = GForm $ \env fe -> do (GForm f) <*> (GForm g) = GForm $ do
(f1, f2, f3) <- f env fe (f1, f2, f3) <- f
(g1, g2, g3) <- g env fe (g1, g2, g3) <- g
return (f1 <*> g1, f2 `mappend` g2, f3 `mappend` g3) return (f1 <*> g1, f2 `mappend` g2, f3 `mappend` g3)
-- | Create a required field (ie, one that cannot be blank) from a -- | Create a required field (ie, one that cannot be blank) from a
-- 'FieldProfile'.ngs -- 'FieldProfile'.ngs
requiredFieldHelper :: FieldProfile sub y a -> FormFieldSettings requiredFieldHelper :: FieldProfile sub y a -> FormFieldSettings
-> Maybe a -> FormField sub y a -> Maybe a -> FormField sub y a
requiredFieldHelper (FieldProfile parse render mkWidget) ffs orig = requiredFieldHelper (FieldProfile parse render mkWidget) ffs orig = GForm $ do
GForm $ \env _ -> do env <- lift ask
let (FormFieldSettings label tooltip theId' name') = ffs let (FormFieldSettings label tooltip theId' name') = ffs
name <- maybe newFormIdent return name' name <- maybe newFormIdent return name'
theId <- maybe newFormIdent return theId' theId <- maybe newFormIdent return theId'
@ -158,8 +167,8 @@ requiredFieldHelper (FieldProfile parse render mkWidget) ffs orig =
-- 'FieldProfile'. -- 'FieldProfile'.
optionalFieldHelper :: FieldProfile sub y a -> FormFieldSettings optionalFieldHelper :: FieldProfile sub y a -> FormFieldSettings
-> FormletField sub y (Maybe a) -> FormletField sub y (Maybe a)
optionalFieldHelper (FieldProfile parse render mkWidget) ffs orig' = optionalFieldHelper (FieldProfile parse render mkWidget) ffs orig' = GForm $ do
GForm $ \env _ -> do env <- lift ask
let (FormFieldSettings label tooltip theId' name') = ffs let (FormFieldSettings label tooltip theId' name') = ffs
let orig = join orig' let orig = join orig'
name <- maybe newFormIdent return name' name <- maybe newFormIdent return name'
@ -191,8 +200,8 @@ fieldsToInput = map fiInput
-- | Convert the XML in a 'GForm'. -- | Convert the XML in a 'GForm'.
mapFormXml :: (xml1 -> xml2) -> GForm s y xml1 a -> GForm s y xml2 a mapFormXml :: (xml1 -> xml2) -> GForm s y xml1 a -> GForm s y xml2 a
mapFormXml f (GForm g) = GForm $ \e fe -> do mapFormXml f (GForm g) = GForm $ do
(res, xml, enc) <- g e fe (res, xml, enc) <- g
return (res, f xml, enc) return (res, f xml, enc)
-- | Using this as the intermediate XML representation for fields allows us to -- | Using this as the intermediate XML representation for fields allows us to
@ -233,10 +242,19 @@ type FormletField sub y a = Maybe a -> FormField sub y a
type FormInput sub y = GForm sub y [GWidget sub y ()] type FormInput sub y = GForm sub y [GWidget sub y ()]
checkForm :: (a -> FormResult b) -> GForm s m x a -> GForm s m x b checkForm :: (a -> FormResult b) -> GForm s m x a -> GForm s m x b
checkForm f (GForm form) = GForm $ \env fenv -> do checkForm f (GForm form) = GForm $ do
(res, xml, enc) <- form env fenv (res, xml, enc) <- form
let res' = case res of let res' = case res of
FormSuccess a -> f a FormSuccess a -> f a
FormFailure e -> FormFailure e FormFailure e -> FormFailure e
FormMissing -> FormMissing FormMissing -> FormMissing
return (res', xml, enc) return (res', xml, enc)
askParams :: Monad m => StateT Ints (ReaderT Env m) Env
askParams = lift ask
askFiles :: Monad m => StateT Ints (ReaderT Env (ReaderT FileEnv m)) FileEnv
askFiles = lift $ lift ask
liftForm :: Monad m => m a -> StateT Ints (ReaderT Env (ReaderT FileEnv m)) a
liftForm = lift . lift . lift

View File

@ -84,7 +84,8 @@ maybeTimeField :: FormFieldSettings -> FormletField sub y (Maybe TimeOfDay)
maybeTimeField = optionalFieldHelper timeFieldProfile maybeTimeField = optionalFieldHelper timeFieldProfile
boolField :: FormFieldSettings -> Maybe Bool -> FormField sub y Bool boolField :: FormFieldSettings -> Maybe Bool -> FormField sub y Bool
boolField ffs orig = GForm $ \env _ -> do boolField ffs orig = GForm $ do
env <- askParams
let label = ffsLabel ffs let label = ffsLabel ffs
tooltip = ffsTooltip ffs tooltip = ffsTooltip ffs
name <- maybe newFormIdent return $ ffsName ffs name <- maybe newFormIdent return $ ffsName ffs
@ -118,7 +119,8 @@ maybeHtmlField = optionalFieldHelper htmlFieldProfile
selectField :: Eq x => [(x, String)] selectField :: Eq x => [(x, String)]
-> FormFieldSettings -> FormFieldSettings
-> Maybe x -> FormField sub master x -> Maybe x -> FormField sub master x
selectField pairs ffs initial = GForm $ \env _ -> do selectField pairs ffs initial = GForm $ do
env <- askParams
let label = ffsLabel ffs let label = ffsLabel ffs
tooltip = ffsTooltip ffs tooltip = ffsTooltip ffs
theId <- maybe newFormIdent return $ ffsId ffs theId <- maybe newFormIdent return $ ffsId ffs
@ -159,7 +161,8 @@ selectField pairs ffs initial = GForm $ \env _ -> do
maybeSelectField :: Eq x => [(x, String)] maybeSelectField :: Eq x => [(x, String)]
-> FormFieldSettings -> FormFieldSettings
-> FormletField sub master (Maybe x) -> FormletField sub master (Maybe x)
maybeSelectField pairs ffs initial' = GForm $ \env _ -> do maybeSelectField pairs ffs initial' = GForm $ do
env <- askParams
let initial = join initial' let initial = join initial'
label = ffsLabel ffs label = ffsLabel ffs
tooltip = ffsTooltip ffs tooltip = ffsTooltip ffs
@ -209,10 +212,13 @@ maybeStringInput n =
optionalFieldHelper stringFieldProfile (nameSettings n) Nothing optionalFieldHelper stringFieldProfile (nameSettings n) Nothing
boolInput :: String -> FormInput sub master Bool boolInput :: String -> FormInput sub master Bool
boolInput n = GForm $ \env _ -> return boolInput n = GForm $ do
(FormSuccess $ fromMaybe "" (lookup n env) /= "", return $ addBody [$hamlet| env <- askParams
let res = FormSuccess $ fromMaybe "" (lookup n env) /= ""
let xml = addBody [$hamlet|
%input#$n$!type=checkbox!name=$n$ %input#$n$!type=checkbox!name=$n$
|], UrlEncoded) |]
return (res, [xml], UrlEncoded)
dayInput :: String -> FormInput sub master Day dayInput :: String -> FormInput sub master Day
dayInput n = dayInput n =

View File

@ -10,12 +10,14 @@ module Yesod.Form.Profiles
, emailFieldProfile , emailFieldProfile
, urlFieldProfile , urlFieldProfile
, doubleFieldProfile , doubleFieldProfile
, fileFieldProfile
, parseDate , parseDate
, parseTime , parseTime
) where ) where
import Yesod.Form.Core import Yesod.Form.Core
import Yesod.Widget import Yesod.Widget
import Yesod.Request
import Text.Hamlet import Text.Hamlet
import Data.Time (Day, TimeOfDay(..)) import Data.Time (Day, TimeOfDay(..))
import qualified Data.ByteString.Lazy.UTF8 as U import qualified Data.ByteString.Lazy.UTF8 as U
@ -45,6 +47,9 @@ doubleFieldProfile = FieldProfile
|] |]
} }
fileFieldProfile :: FieldProfile s m FileInfo
fileFieldProfile = undefined -- FIXME
dayFieldProfile :: FieldProfile sub y Day dayFieldProfile :: FieldProfile sub y Day
dayFieldProfile = FieldProfile dayFieldProfile = FieldProfile
{ fpParse = parseDate { fpParse = parseDate