[Haskell-cafe] Problem with monadic formlets
Colin Paul Adams
colin at colina.demon.co.uk
Fri Aug 28 02:06:05 EDT 2009
>>>>> "Jeremy" == Jeremy Shaw <jeremy at n-heptane.com> writes:
Jeremy> Hello, I hacked your code into a runnable example, and it
Jeremy> seems to work for me.
Jeremy> Which looks correct to me. Your code looks fine to me as
Jeremy> well... Perhaps the error is not in the code you pasted,
Jeremy> but somewhere else. I am running on an older, and somewhat
Jeremy> forked version of Formlets, so there could also be a bug
Jeremy> in the new code I guess. Though, that seems unlikely. But
Jeremy> it is worth noting that we are not using the same version
Jeremy> of the formlets library.
I did some debugging in ghci, but was unable to step through the
ensure and check routines, which is where the apparent data corruprion
is occurring. I am suspecting a bug in the formlets library (I have
version 0.6).
So I have created a slightly cut-down (no database involved) complete
working program. Can you see if this works ok with your version of
formlets:
module Main where
import Control.Applicative
import Control.Applicative.Error
import Control.Applicative.State
import Data.List as List
import Text.Formlets
import qualified Text.XHtml.Strict.Formlets as F
import qualified Text.XHtml.Strict as X
import Text.XHtml.Strict ((+++), (<<))
import Happstack.Server
type XForm a = F.XHtmlForm IO a
data Registration = Registration { regUser :: String
, regPass :: String }
deriving Show
handleRegistration :: ServerPartT IO Response
handleRegistration = withForm "register" register showErrorsInline (\u -> okHtml $ regUser u ++ " is successfully registered")
withForm :: String -> XForm a -> (X.Html -> [String] -> ServerPartT IO Response) -> (a -> ServerPartT IO Response) -> ServerPartT IO Response
withForm name frm handleErrors handleOk = dir name $ msum
[ methodSP GET $ createForm [] frm >>= okHtml
, withDataFn lookPairs $ \d ->
methodSP POST $ handleOk' $ simple d
]
where
handleOk' d = do
let (extractor, html, _) = runFormState d frm
v <- liftIO extractor
case v of
Failure faults -> do
f <- createForm d frm
handleErrors f faults
Success s -> handleOk s
simple d = List.map (\(k,v) -> (k, Left v)) d
showErrorsInline :: X.Html -> [String] -> ServerPartT IO Response
showErrorsInline renderedForm errors =
okHtml $ X.toHtml (show errors) +++ renderedForm
createForm :: Env -> XForm a -> ServerPartT IO X.Html
createForm env frm = do
let (extractor, xml, endState) = runFormState env frm
xml' <- liftIO xml
return $ X.form X.! [X.method "POST"] << (xml' +++ X.submit "submit" "Submit")
okHtml :: (X.HTML a) => a -> ServerPartT IO Response
okHtml content = ok $ toResponse $ htmlPage $ content
htmlPage :: (X.HTML a) => a -> X.Html
htmlPage content = (X.header << (X.thetitle << "Testing forms"))
+++ (X.body << content)
register :: XForm Registration
register = Registration <$> user <*> passConfirmed
user :: XForm String
user = pure_user `F.checkM` F.ensureM valid error where
valid name = return True
error = "Username already exists in the database!"
pure_user :: XForm String
pure_user = input `F.check` F.ensure valid error where
input = "Username" `label` F.input Nothing
valid = (>= 3) . length
error = "Username must be three characters or longer."
passConfirmed :: XForm String
passConfirmed = fst <$> passwords `F.check` F.ensure equal error where
passwords = (,) <$> pass "Password" <*> pass "Password (confirm)"
equal (a, b) = a == b
error = "The entered passwords do not match!"
pass :: String -> XForm String
pass caption = input `F.check` F.ensure valid error where
input = caption `label` F.password Nothing
valid = (>=6) . length
error = "Password must be six characters or longer."
label :: String -> XForm String -> XForm String
label l = F.plug (\xhtml -> X.p << (X.label << (l ++ ": ") +++ xhtml))
main = simpleHTTP (nullConf {port = 9959}) handleRegistration
--
Colin Adams
Preston Lancashire
More information about the Haskell-Cafe
mailing list