packages feed

formlets 0.2 → 0.2.1

raw patch · 5 files changed

+161/−164 lines, 5 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Text.Formlets.Base: check :: Form xml a -> (a -> Failing b) -> Form xml b
- Text.Formlets.Base: class Plus a
- Text.Formlets.Base: data Form xml a
- Text.Formlets.Base: ensure :: (Show a) => (a -> Bool) -> String -> a -> Failing a
- Text.Formlets.Base: ensures :: (Show a) => [(a -> Bool, String)] -> a -> Failing a
- Text.Formlets.Base: fmapFst :: (t -> t2) -> (t, t1) -> (t2, t1)
- Text.Formlets.Base: input' :: (String -> String -> xml) -> Maybe String -> Form xml String
- Text.Formlets.Base: instance (Plus xml) => Applicative (Form xml)
- Text.Formlets.Base: instance (Plus xml) => Functor (Form xml)
- Text.Formlets.Base: plug :: (Plus xml) => (xml -> xml) -> Form xml a -> Form xml a
- Text.Formlets.Base: plus :: (Plus a) => a -> a -> a
- Text.Formlets.Base: runFormState :: Env -> Form xml a -> (Collector (Failing a), xml)
- Text.Formlets.Base: type Env = [(String, String)]
- Text.Formlets.Base: xml :: xml -> Form xml ()
- Text.Formlets.Base: zero :: (Plus a) => a
- Text.Formlets.Widgets: enumRadio :: (Enum a) => [(a, String)] -> Maybe a -> XHtmlForm a
- Text.Formlets.Widgets: hidden :: Maybe String -> XHtmlForm String
- Text.Formlets.Widgets: input :: Maybe String -> XHtmlForm String
- Text.Formlets.Widgets: inputIntegerF :: Maybe Integer -> XHtmlForm Integer
- Text.Formlets.Widgets: instance Plus Html
- Text.Formlets.Widgets: password :: Maybe String -> XHtmlForm String
- Text.Formlets.Widgets: radio :: [(String, String)] -> Maybe String -> XHtmlForm String
- Text.Formlets.Widgets: textarea :: Maybe String -> XHtmlForm String
- Text.Formlets.Widgets: type XHtmlForm a = Form Html a
+ Text.Formlets: check :: Form xml a -> (a -> Failing b) -> Form xml b
+ Text.Formlets: class Plus a
+ Text.Formlets: data Form xml a
+ Text.Formlets: ensure :: (Show a) => (a -> Bool) -> String -> a -> Failing a
+ Text.Formlets: ensures :: (Show a) => [(a -> Bool, String)] -> a -> Failing a
+ Text.Formlets: fmapFst :: (t -> t2) -> (t, t1) -> (t2, t1)
+ Text.Formlets: input' :: (String -> String -> xml) -> Maybe String -> Form xml String
+ Text.Formlets: instance (Plus xml) => Applicative (Form xml)
+ Text.Formlets: instance Functor (Form xml)
+ Text.Formlets: plug :: (Plus xml) => (xml -> xml) -> Form xml a -> Form xml a
+ Text.Formlets: plus :: (Plus a) => a -> a -> a
+ Text.Formlets: runFormState :: Env -> Form xml a -> (Collector (Failing a), xml)
+ Text.Formlets: type Env = [(String, String)]
+ Text.Formlets: xml :: xml -> Form xml ()
+ Text.Formlets: zero :: (Plus a) => a
+ Text.XHtml.Strict.Formlets: enumRadio :: (Enum a) => [(a, String)] -> Maybe a -> XHtmlForm a
+ Text.XHtml.Strict.Formlets: hidden :: Maybe String -> XHtmlForm String
+ Text.XHtml.Strict.Formlets: input :: Maybe String -> XHtmlForm String
+ Text.XHtml.Strict.Formlets: inputInteger :: Maybe Integer -> XHtmlForm Integer
+ Text.XHtml.Strict.Formlets: instance Plus Html
+ Text.XHtml.Strict.Formlets: password :: Maybe String -> XHtmlForm String
+ Text.XHtml.Strict.Formlets: radio :: [(String, String)] -> Maybe String -> XHtmlForm String
+ Text.XHtml.Strict.Formlets: textarea :: Maybe String -> XHtmlForm String
+ Text.XHtml.Strict.Formlets: type XHtmlForm a = Form Html a

Files

Text/Formlets.hs view
@@ -1,4 +1,108 @@-module Text.Formlets (module W, module B) where+module Text.Formlets ( input', fmapFst+                     , check, ensure, ensures+                     , runFormState +                     , xml, plug+                     , Env , Form , Plus (..)+                     )+                     where -import Text.Formlets.Widgets as W-import Text.Formlets.Base as B+import Control.Applicative+import Control.Applicative.Error+import Control.Applicative.State+import Data.Maybe (isJust)++class Plus a where+  zero :: a+  plus :: a -> a -> a++-- | Apply a predicate to a value and return Success or Failure as appropriate+ensure :: Show a +       => (a -> Bool) -- ^ The predicate+       -> String      -- ^ The error message, in case the predicate fails+       -> a           -- ^ The value+       -> Failing a+ensure p msg x | p x = Success x+               | otherwise = Failure [msg]++-- | Apply multiple predicates to a value, return Success or all the Failure messages+ensures :: Show a+        => [(a -> Bool, String)] -- ^ List of predicate functions and error messages, in case the predicate fails+        -> a                     -- ^ The value+        -> Failing a+ensures ps x | null errors = Success x+             | otherwise   = Failure errors+    where errors = [ err | (p, err) <- ps, not $ p x ]++-- | Helper function for genereting input components based forms.+input' :: (String -> String -> xml) -> Maybe String -> Form xml String+input' i defaultValue = Form $ \env -> mkInput env <$> freshName+   where mkInput env name = (Success . (`queryParam` (name)),+                             i name (value name env))+         value name env = maybe (maybe "" id defaultValue) id (lookup name env)++-- | Runs the form state+runFormState :: Env             -- ^ A previously filled environment (may be empty)+             -> Form xml a      -- ^ The form+             -> (Collector (Failing a), xml)+runFormState e (Form f) = evalState (f e) 0++-- | Add additional validation to an already validated component+check :: Form xml a -> (a -> Failing b) -> Form xml b+check (Form frm) f = Form $ \e -> checker (frm e)+ where checker = fmap $ fmapFst (f' .)+       f' (Failure x) = Failure x+       f' (Success x) = f x++--- Form stuff+type Env = [(String, String)]+type FormState = Names+type Names = Integer+type Name = String++queryParam :: Env -> Name -> String+queryParam env name = case (name `lookup` env) of+                           Nothing -> error $ "Couldn't find " ++ name+                           Just x  -> x+++newtype Form xml a = Form { deform :: Env -> State FormState (Collector (Failing a), xml) }++instance Functor (Form xml) where+  fmap f (Form a) = Form $ \env -> (fmap . fmapFst . fmap . fmap) f (a env)++fmapFst f (a, b) = (f a, b)++type Collector a = Env -> a++instance Plus xml => Applicative (Form xml) where+   pure = pureF+   (<*>) = applyF++pureF :: Plus xml => a -> Form xml a+pureF v = Form $ \env -> pure (const (Success v), zero) -- K++applyF :: Plus xml => Form xml (a -> b) -> Form xml a -> Form xml b+(Form f) `applyF` (Form v) = Form $ \env -> pure combine <*> f env <*> v env+  where combine (v1, xml1) (v2, xml2) = (\e -> v1 e <*> v2 e, xml1 `plus` xml2)+++-- | Component: just some xml+xml :: xml -> Form xml ()+xml x = Form $ \env -> pure (const $ Success (), x)++-- | Transform the XML component+plug :: Plus xml => (xml -> xml) -> Form xml a -> Form xml a+f `plug` (Form m) = Form $ \env -> pure plugin <*> m env+   where plugin (c, x) = (c, f x)++-----------------------------------------------+-- Private methods+-----------------------------------------------++freshName :: State FormState String+freshName = do n <- currentName+               modify (+1)+               return n++currentName :: State FormState String+currentName = gets $ (++) "input" . show
− Text/Formlets/Base.hs
@@ -1,110 +0,0 @@-{-# LANGUAGE TypeOperators #-}-module Text.Formlets.Base (-                     input', fmapFst-                     , check, ensure, ensures-                     , runFormState -                     , xml, plug-                     , Env , Form , Plus (..)-                     )-                     where--import Control.Applicative-import Control.Applicative.Error-import Control.Applicative.State-import Data.Maybe (isJust)--class Plus a where-  zero :: a-  plus :: a -> a -> a---- | Apply a predicate to a value and return Success or Failure as appropriate-ensure :: Show a -       => (a -> Bool) -- ^ The predicate-       -> String      -- ^ The error message, in case the predicate fails-       -> a           -- ^ The value-       -> Failing a-ensure p msg x | p x = Success x-               | otherwise = Failure [msg]---- | Apply multiple predicates to a value, return Success or all the Failure messages-ensures :: Show a-        => [(a -> Bool, String)] -- ^ List of predicate functions and error messages, in case the predicate fails-        -> a                     -- ^ The value-        -> Failing a-ensures ps x | null errors = Success x-             | otherwise   = Failure errors-    where errors = [ err | (p, err) <- ps, not $ p x ]---- | Helper function for genereting input components based forms.-input' :: (String -> String -> xml) -> Maybe String -> Form xml String-input' i defaultValue = Form $ \env -> mkInput env <$> freshName-   where mkInput env name = (Success . (`queryParam` (name)),-                             i name (value name env))-         value name env = maybe (maybe "" id defaultValue) id (lookup name env)---- | Runs the form state-runFormState :: Env             -- ^ A previously filled environment (may be empty)-             -> Form xml a      -- ^ The form-             -> (Collector (Failing a), xml)-runFormState e (Form f) = evalState (f e) 0---- | Add additional validation to an already validated component-check :: Form xml a -> (a -> Failing b) -> Form xml b-check (Form frm) f = Form $ \e -> checker (frm e)- where checker = fmap $ fmapFst (f' .)-       f' (Failure x) = Failure x-       f' (Success x) = f x----- Form stuff-type Env = [(String, String)]-type FormState = Names-type Names = Integer-type Name = String--queryParam :: Env -> Name -> String-queryParam env name = case (name `lookup` env) of-                           Nothing -> error $ "Couldn't find " ++ name-                           Just x  -> x---newtype Form xml a = Form { deform :: Env -> State FormState (Collector (Failing a), xml) }--instance Plus xml => Functor (Form xml) where-  fmap f (Form a) = Form $ \env -> (fmap . fmapFst . fmap . fmap) f (a env)--fmapFst f (a, b) = (f a, b)--type Collector a = Env -> a--instance Plus xml => Applicative (Form xml) where-   pure = pureF-   (<*>) = applyF--pureF :: Plus xml => a -> Form xml a-pureF v = Form $ \env -> pure (const (Success v), zero) -- K--applyF :: Plus xml => Form xml (a -> b) -> Form xml a -> Form xml b-(Form f) `applyF` (Form v) = Form $ \env -> pure combine <*> f env <*> v env-  where combine (v1, xml1) (v2, xml2) = (\e -> v1 e <*> v2 e, xml1 `plus` xml2)----- | Component: just some xml-xml :: xml -> Form xml ()-xml x = Form $ \env -> pure (const $ Success (), x)---- | Transform the XML component-plug :: Plus xml => (xml -> xml) -> Form xml a -> Form xml a-f `plug` (Form m) = Form $ \env -> pure plugin <*> m env-   where plugin (c, x) = (c, f x)---------------------------------------------------- Private methods--------------------------------------------------freshName :: State FormState String-freshName = do n <- currentName-               modify (+1)-               return n--currentName :: State FormState String-currentName = gets $ (++) "input" . show
− Text/Formlets/Widgets.hs
@@ -1,48 +0,0 @@-module Text.Formlets.Widgets where--import Text.Formlets.Base-import qualified Text.XHtml.Strict as X-import Text.XHtml.Strict ((!), (+++))-import Control.Applicative.Error--type XHtmlForm a = Form X.Html a--instance Plus X.Html where-  zero = X.noHtml-  plus = (+++)---- | An input field with an optional value-input :: Maybe String -> XHtmlForm String-input = input' (\n v -> X.textfield n ! [X.value v])--textarea :: Maybe String -> XHtmlForm String-textarea = input' (\n v -> X.textarea (X.toHtml v) ! [X.name n])---- | A password field with an optional value-password :: Maybe String -> XHtmlForm String-password = input' (\n v -> X.password n ! [X.value v])---- | A hidden input field-hidden  :: Maybe String -> XHtmlForm String-hidden  =  input' X.hidden---- | A validated integer component-inputIntegerF :: Maybe Integer -> XHtmlForm Integer-inputIntegerF x = input (fmap show x) `check` asInteger ---- | A radio choice-radio :: [(String, String)] -> Maybe String -> XHtmlForm String-radio choices = input' mkRadios -- todo: validate that the result was in the choices- where radio n v i = X.input ! [X.thetype "radio", X.name n, X.identifier i, X.theclass "radio", X.value v]-       mkRadios name selected = X.concatHtml $ map (mkRadio name selected) (zip choices [1..])-       mkRadio  name selected ((value, label), idx) = (radio name value ident) ! attrs -                                                   +++ X.label (X.toHtml label) ! [X.thefor ident, X.theclass "radio"]-        where attrs | selected == value = [X.checked]-                    | otherwise = []-              ident = name ++ "_" ++ show idx--enumRadio :: (Enum a) => [(a, String)] -> Maybe a -> XHtmlForm a-enumRadio values defaultValue = radio (map toS values) (fmap (show . fromEnum) defaultValue) -                                `check` convert `check` tryToEnum- where toS = fmapFst (show . fromEnum)-       convert v = maybeRead' v "Conversion error" 
+ Text/XHtml/Strict/Formlets.hs view
@@ -0,0 +1,52 @@+module Text.XHtml.Strict.Formlets ( input, textarea, password+                                  , hidden, inputInteger, radio, enumRadio+                                  , XHtmlForm+                                  , module Text.Formlets+                                  ) where++import Text.Formlets+import qualified Text.XHtml.Strict as X+import Text.XHtml.Strict ((!), (+++))+import Control.Applicative.Error++type XHtmlForm a = Form X.Html a++instance Plus X.Html where+  zero = X.noHtml+  plus = (+++)++-- | An input field with an optional value+input :: Maybe String -> XHtmlForm String+input = input' (\n v -> X.textfield n ! [X.value v])++textarea :: Maybe String -> XHtmlForm String+textarea = input' (\n v -> X.textarea (X.toHtml v) ! [X.name n])++-- | A password field with an optional value+password :: Maybe String -> XHtmlForm String+password = input' (\n v -> X.password n ! [X.value v])++-- | A hidden input field+hidden  :: Maybe String -> XHtmlForm String+hidden  =  input' X.hidden++-- | A validated integer component+inputInteger :: Maybe Integer -> XHtmlForm Integer+inputInteger x = input (fmap show x) `check` asInteger ++-- | A radio choice+radio :: [(String, String)] -> Maybe String -> XHtmlForm String+radio choices = input' mkRadios -- todo: validate that the result was in the choices+ where radio n v i = X.input ! [X.thetype "radio", X.name n, X.identifier i, X.theclass "radio", X.value v]+       mkRadios name selected = X.concatHtml $ map (mkRadio name selected) (zip choices [1..])+       mkRadio  name selected ((value, label), idx) = (radio name value ident) ! attrs +                                                   +++ X.label (X.toHtml label) ! [X.thefor ident, X.theclass "radio"]+        where attrs | selected == value = [X.checked]+                    | otherwise = []+              ident = name ++ "_" ++ show idx++enumRadio :: (Enum a) => [(a, String)] -> Maybe a -> XHtmlForm a+enumRadio values defaultValue = radio (map toS values) (fmap (show . fromEnum) defaultValue) +                                `check` convert `check` tryToEnum+ where toS = fmapFst (show . fromEnum)+       convert v = maybeRead' v "Conversion error" 
formlets.cabal view
@@ -1,5 +1,5 @@ Name:            formlets-Version:         0.2+Version:         0.2.1 Synopsis:        Formlets implemented in Haskell Description:     A modular way to build forms based on applicative functors, as                  described in:@@ -15,7 +15,6 @@ Author:          Jeremy Yallop / Chris Eidhof Maintainer:      Chris Eidhof <ce+hackage@tupil.com> Exposed-Modules:   Text.Formlets-                 , Text.Formlets.Base-                 , Text.Formlets.Widgets+                 , Text.XHtml.Strict.Formlets Build-Type:      Simple Build-Depends:   base, haskell98, mtl, xhtml, applicative-extras