-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Web application server with stateful, type safe user interactions and widget combinators. -- -- Simple application server with stateful request-response flows, -- persistent and transparent session handling. server process -- management, combinators for the definition of widgets and formlets -- that can be mixed freely with HTML formatting and produce statically -- typed web applications. Adopt and extend the best formlet/applicative -- Haskell traditions Console and window oriented apps are possible. -- -- MFlow (MessageFlow) was created initially as the user interface for -- the Workflow package Currently is an alpha version. It has only basic -- authentication but I plan to inprove it for serious applications. -- -- It includes Application Server features such is resource an process -- management and automatic recovery -- -- Resource management: The user can define process and session timeout. -- The process is automatically rerun after timeout if a new request -- arrive with transparent recovery of state, at the point of the -- interrupted dialog even after server crash. -- -- The backend operation relies on the Workflow package -- (http://hackage.haskell.org/package/Workflow/index), this gives -- transparent sessión persistence and recovery, all of this is supported -- by TCache (http://hackage.haskell.org/package/TCache/index), -- that gives backend-independent transactions and can be used directly -- by the programmer. Persistence in files for session and data out of -- the box enables very fast prototyping. -- -- All the plumbing is hidden to the programmer, There are no methods for -- session management, database query, recovery and so on. All of this is -- transparent So the surface exposed to the programmer is minimal. -- -- Includes generalized formlets that permits the mix of active widgets -- in the same page while remaining statically typed and, thus the -- programs can verify correctness at compilation time. -- -- Includes combinators for seamless inclusion of these widgets within -- user defined HTML formatting. Bindings for Text.XHtml. The widget -- generation may be easy for user with familiarity with -- formlets/digestive functors and Text.XHtml formatting. -- -- Currently it has bindings for the Hack interface -- -- Streaming facilities. -- -- To do: -- -- Bindings for HSP -- -- Clustering -- -- Other bindigs for Hack alternatives @package MFlow @version 0.0.4 -- | This module defines an integrated way to interact with the user. -- ask is a single method of user interaction. it send user -- interfaces and return statically typed responses. The user interface -- definitions are based on the formLets interface -- -- But additionally, unlike formLets in its current form, it permits the -- definition of widgets. A widget is data that, when renderized and -- interact with the user, return data, just like a formlet, but it hasn -- to be an HTML form. it can contain JavaScript, or additional Html -- decoration or it can use Ajax istead of form post for the interaction. -- There is an example of widget defined (Selection) -- -- widgets (and formlets) can be combined in a sigle Html page. Here is a -- ready-to-run example that combines a Widget (Selection) and a HTML -- decorated formLet in the same page. -- --
-- import MFlow.Hack.XHtml.All
--
-- import Data.Typeable
-- import Control.Monad.Trans
-- import qualified Data.Vector as V
--
-- main= do
--
-- putStrLn $ options messageFlows
-- run 80 $ hackMessageFlow messageFlows
-- where
-- messageFlows= [("main", runFlow mainProds )
-- ,("hello", stateless hello)]
-- options msgs= "in the browser choose\n\n" ++
-- concat [ http://server/++ i ++ n | (i,_) <- msgs]
--
-- --an stateless procedure, as an example
-- hello :: Env -> IO String
-- hello env = return "hello, this is a stateless response"
--
-- data Prod= Prod{pname :: String, pprice :: Int} deriving (Typeable,Read,Show)
--
-- -- formLets can have Html formatting. Additional operators <++ <+> <<< ++> to XHtml formatting
--
-- instance FormLet Prod IO Html where
-- digest mp= table <<< (
-- Prod <$> tr <<< (td << "enter the name" <++ td <<< getString (pname <$> mp))
-- <*> tr <<< (td << "enter the price" <++ td <<< getInt ( pprice <$> mp)))
--
-- -- Here an example of predefined widget (Selection) that return an Int, combined in the same
-- -- page with the fromLet for the introduction of a product.
-- -- The result of the user interaction is Either one or the other value
--
-- shopProds :: V.Vector Int -> [Prod]
-- -> View Html IO (Either Int Prod)
-- shopProds cart products=
--
-- p << "----------------Shopping List--------------"
-- <++
-- widget(Selection{
-- stitle = bold << "choose an item",
-- sheader= [ bold << "item" , bold << "price", bold << "times chosen"],
-- sbody= [([toHtml pname, toHtml $ show pprice, toHtml $ show $ cart V.! i],i )
-- | (Prod{..},i ) <- zip products [1..]]})
--
-- <+>
-- p << "--------------Add a new product ---------------"
-- <++
-- table <<< (tr <<< td ! [valign "top"]
-- <<< widget (Form (Nothing :: Maybe Prod) )
-- ++>
-- tr << td ! [align "center"]
-- << hotlink "hello"
-- (bold << "Hello World"))
--
-- -- the header
--
-- appheader user forms= thehtml
-- << body << dlist << (concatHtml
-- [dterm <<("Hi "++ user)
-- ,dterm << "This example contains two forms enclosed within user defined HTML formatting"
-- ,dterm << "The first one is defined as a Widget, the second is a formlet formatted within a table"
-- ,dterm << "both are defined using an extension of the FormLets concept"
-- ,dterm << "the form results are statically typed"
-- ,dterm << "The state is implicitly logged. No explicit handling of state"
-- ,dterm << "The program logic is written as a procedure. Not in request-response form. But request response is possible"
-- ,dterm << "lifespan of the serving process and the execution state defined by the programmer"
-- ,dterm << "user state is automatically recovered after cold re-start"
-- ,dterm << "transient, non persistent states possible."
-- ])
-- +++ forms
--
-- -- Here the procedure. It ask for either entering a new product
-- -- or to "buy" one of the entered products.
-- -- There is a timeout of ten minutes before the process is stopped
-- -- THERE IS A timeout of one day for the whole state so after this, the
-- -- user will see the list erased.
-- -- The state is user specific.
--
-- --mainProds :: FlowM Html (Workflow IO) ()
-- mainProds = do
-- setTimeouts (10*60) (24*60*60)
-- setHeader $ w -> bold << "Please enter userpassword (pepepepe)" +++ br +++ w
--
-- setHeader $ appheader user
-- mainProds1 [] $ V.fromList [0]
-- where
-- mainProds1 prods cart= do
-- mr <- step . ask $ shopProds cart prods
-- case mr of
-- Right prod -> mainProds1 (prod:prods) (V.snoc cart 0)
-- Left i -> do
-- let newCart= cart V. [(i, cart V.! i + 1 )]
-- mainProds1 prods newCart
--
module MFlow.Forms
class (Functor m, MonadIO m) => Widget a b m view | a -> view
widget :: Widget a b m view => a -> View view m b
class (Functor m, MonadIO m) => FormLet a m view
digest :: FormLet a m view => Maybe a -> View view m a
-- | Launchable widgets create user requests. For example whatever piece
-- containing a Form tag, a link with an embeeded Ajax invocation etc.
--
-- A FormLet for an input field can not be an instance of Launchable, for
-- example to invoke it with ask, make the widget an instance of
-- Launchable
class Widget a b m view => Launchable a b m view
type View view m a = FormT view (FlowM view m) a
-- | Minimal interface for defining the abstract basic form combinators
-- defined in this module. see MFlow.Forms.XHtml for the instance
-- for Text.XHtml format
class FormInput view where flink1 verb = flink verb (fromString verb) foption1 name list msel = foption name (zip list list) msel
inred :: FormInput view => view -> view
ftable :: FormInput view => view -> [view] -> [[view]] -> view
fromString :: FormInput view => String -> view
flink :: FormInput view => String -> view -> view
flink1 :: FormInput view => String -> view
finput :: FormInput view => Name -> Type -> Value -> Checked -> OnClick -> view
ftextarea :: FormInput view => String -> String -> view
foption :: FormInput view => String -> [(String, String)] -> Maybe String -> view
foption1 :: FormInput view => String -> [String] -> Maybe String -> view
fformAction :: FormInput view => String -> view -> view
newtype FormT view m a
FormT :: (Params -> m (FormElm view a)) -> FormT view m a
runFormT :: FormT view m a -> Params -> m (FormElm view a)
data FormElm view a
FormElm :: [view] -> (Maybe a) -> FormElm view a
newtype Form a
Form :: a -> Form a
data Selection a view
Selection :: view -> [view] -> [([view], a)] -> Selection a view
stitle :: Selection a view -> view
sheader :: Selection a view -> [view]
sbody :: Selection a view -> [([view], a)]
-- | register an user/password combination
userRegister :: String -> String -> IO ()
-- | authentication against userRegistered users. to be used with
-- validate
userAuthenticate :: MonadIO m => User -> m (Maybe String)
data User
-- | Very basic user authentication. The user is stored in a cookie. it
-- looks for the cookie. If no cookie, it ask to the user for a
-- userRegistered user-password combination. It return a reference
-- to the user.
getUser :: (FormInput view, Monoid view, Typeable view, ConvertTo (HttpData view) display, Typeable display, MonadIO m, Functor m) => FlowM view m String
-- | it is the way to interact with the user. It takes a combination of
-- launchable objects and return the user result in the FlowM
-- monad
ask :: (Launchable a b m view, FormInput view, Monoid view, Typeable view, ConvertTo (HttpData view) display, Typeable display) => a -> FlowM view m b
getString :: (FormInput view, Monad m) => Maybe String -> View view m String
getInt :: (FormInput view, Functor m, MonadIO m) => Maybe Int -> View view m Int
getInteger :: (FormInput view, Functor m, MonadIO m) => Maybe Integer -> View view m Integer
getMultilineText :: (FormInput view, Monad m) => Maybe [Char] -> View view m String
getBool :: (FormInput view, Monad m) => Maybe String -> String -> String -> View view m Bool
getOption :: (FormInput view, Monad m) => Maybe String -> [(String, String)] -> View view m String
getPassword :: (FormInput view, Monad m) => View view m String
-- | Validates a form or widget result against a validating procedure
--
-- getOdd= getInt Nothing validate (x-> return $ if mod x 2==0
-- then Nothing else Just only odd number please)
validate :: (FormInput view, Functor m, MonadIO m) => View view m a -> (a -> m (Maybe String)) -> View view m a
-- | join two widgets in the same pages the resulting widget, when
-- asked with it, returns a either one or the other
mix :: (FormInput view, Monad m) => View view m a' -> View view m b' -> View view m (Either a' b')
-- | encloses instances of Widget or FormLet in formating
-- view is intended to be instantiated to a particular format see
-- MFlow.Forms.XHtml for usage examples
wrap :: (Monad m, FormInput view, Monoid view) => (view -> view) -> View view m a -> View view m a
-- | append formatting to Widget or FormLet instances view is
-- intended to be instantiated to a particular format see
-- MFlow.Forms.XHtml for usage examples
addToForm :: (Monad m, FormInput view, Monoid view) => View view m a -> view -> View view m a
type FlowM view = StateT (MFlowState view)
runFlow :: (FormInput view, Monoid view, Monad m) => FlowM view m () -> Token -> m ()
step :: (Serialize a, MonadIO m, Typeable a) => FlowM view m a -> FlowM view (Workflow m) a
setHeader :: Monad m => (view -> view) -> FlowM view m ()
setTimeouts :: Monad m => Int -> Integer -> FlowM view m ()
-- | set an HTTP cookie
setCookie :: Monad m => String -> String -> String -> Maybe String -> FlowM view m ()
instance [incoherent] Typeable User
instance [incoherent] Read User
instance [incoherent] Show User
instance [incoherent] (MonadIO m, Functor m, FormInput view) => FormLet Bool m view
instance [incoherent] (FormInput view, FormLet a m view, FormLet b m view, FormLet c m view) => FormLet (a, b, c) m view
instance [incoherent] (FormInput view, FormLet a m view, FormLet b m view) => FormLet (a, b) m view
instance [incoherent] FormLet a m view => Widget (Maybe a) a m view
instance [incoherent] (Monad m, Functor m) => Monad (FormT view m)
instance [incoherent] (Functor m, Monad m) => Applicative (FormT view m)
instance [incoherent] Functor m => Functor (FormT view m)
instance [incoherent] Functor (FormElm view)
instance [incoherent] (FormInput view, Monoid view, Widget a b m view) => Widget (Form a) b m view
instance [incoherent] (FormInput view, Monoid view, Widget a b m view) => Launchable (Form a) b m view
instance [incoherent] (MonadIO m, Functor m, FormInput view, Read a, Show a, Eq a, Typeable a) => Widget (Selection a view) a m view
instance [incoherent] (MonadIO m, Functor m, FormInput view, Typeable a, Show a, Read a, Eq a) => Launchable (Selection a view) a m view
instance [incoherent] (MonadIO m, Functor m) => Launchable (View view m a) a m view
instance [incoherent] (MonadIO m, Functor m) => Widget (View view m a) a m view
instance [incoherent] (MonadIO m, Functor m, FormInput view) => FormLet User m view
instance [incoherent] Indexable User
module MFlow.Forms.XHtml
-- | prepend Html formatting to a widget
--
-- -- bold << hi there -- <++ -- widget widget1 --(<++) :: Monad m => Html -> View Html m a -> View Html m a -- | join two widgets in the same pages the resulting widget, when -- asked with it, returns a either one or the other -- --
-- r - ask widget widget1 <+ widget widget2 --(<+>) :: Monad m => View Html m a' -> View Html m b' -> View Html m (Either a' b') -- | append Html to a widget -- --
-- widget widget1 -- ++> -- H1 << hi there --(++>) :: Monad m => View Html m a -> Html -> View Html m a -- | encloses a widget in Html formatting -- --
-- table <<< ( -- tr <<< (td << widget widget1) -- tr <<< (td << widget widget2)) --(<<<) :: Monad m => (Html -> Html) -> View Html m a -> View Html m a instance FormInput Html instance Typeable Html module MFlow.Hack type Cookie = (String, String, String, Maybe String) ctype :: ([Char], [Char]) getCookies :: [([Char], [Char])] -> [([Char], [Char])] cookieHeaders :: [Cookie] -> [([Char], String)] urlDecode :: String -> [([(String, String)], String)] type Params = [(String, String)] getParam1 :: (Typeable a, Read a) => String -> Params -> Maybe a data Req Req :: a -> Req data Resp Fragm :: a -> Resp EndFragm :: a -> Resp Resp :: a -> Resp type Workflow (m :: * -> *) = WF Stat m data HttpData a HttpData :: [Cookie] -> a -> HttpData a class Processable a pwfname :: Processable a => a -> String puser :: Processable a => a -> String pind :: Processable a => a -> String getParams :: Processable a => a -> Params class ConvertTo a b | a -> b convert :: ConvertTo a b => a -> b data Token Token :: String -> String -> String -> TChan Req -> TChan Resp -> Token twfname :: Token -> String tuser :: Token -> String tind :: Token -> String q :: Token -> TChan Req qr :: Token -> TChan Resp getToken :: Processable a => a -> IO Token data Error Error :: String -> Error -- | List of (wfname, workflow) pairs, to be scheduled depending on the -- message's pwfname type ProcList = WorkflowList IO Token () flushRec :: Token -> IO () receive :: (Processable a, Typeable a) => Token -> IO a receiveReq :: Token -> IO Req receiveReqTimeout :: Int -> Integer -> Token -> IO Req -- | send a complete response send :: (Typeable a, Typeable b, ConvertTo a b) => Token -> a -> IO () sendFlush :: (Typeable b, Typeable a, ConvertTo a b) => Token -> a -> IO () -- | send a response fragment. Useful for streaming. the last packet must -- sent trough send sendFragment :: (Typeable a, Typeable b, Monoid b, ConvertTo a b) => Token -> a -> IO () sendEndFragment :: (Typeable a, Typeable b, Monoid b, ConvertTo a b) => Token -> a -> IO () msgScheduler :: (Processable a, Typeable a, ConvertTo Error c, Typeable c) => a -> ProcList -> IO (c, ThreadId) addMessageFlows :: [(String, Token -> Workflow IO ())] -> IO () getMessageFlows :: IO ProcList -- | to add a monadic computation that send and receive messages, but does -- not store its state in permanent storage. transient :: (Token -> IO ()) -> (Token -> Workflow IO ()) stateless :: (Typeable a, Processable a, Typeable b, ConvertTo b c, Typeable c) => (a -> IO b) -> (Token -> Workflow IO ()) -- | An instance of the abstract MFlow scheduler to the Hack -- interface it accept the list of processes being scheduled and return a -- hack handler -- -- Example: -- --
-- main= do
--
-- putStrLn $ options messageFlows
-- run 80 $ hackMessageFlow messageFlows
-- where
-- messageFlows= [("main", runFlow flowname )
-- ,("hello", stateless statelesproc)
-- ,("trans", transient $ runflow transientflow]
-- options msgs= "in the browser choose\n\n" ++
-- concat [ http://server/++ i ++ n | (i,_) <- msgs]
--
hackMessageFlow :: ProcList -> (Env -> IO Response)
instance Typeable Flow
instance Read Flow
instance Show Flow
instance ToResponse v => ConvertTo (HttpData v) TResp
instance ConvertTo Error TResp
instance ConvertTo ByteString TResp
instance ConvertTo String TResp
instance Indexable Flow
instance Processable Env
module MFlow.Hack.XHtml
instance ToResponse Html
module MFlow.Hack.XHtml.All