I Just released MFlow, a 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
Workflow gives transparent sessiĆ³n persistence and recovery, all of this is supported by TCache:
http://hackage.haskell.org/package/TCache
TCache 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.
http://hackage.haskell.org/package/Workflow
Workflow gives transparent sessiĆ³n persistence and recovery, all of this is supported by TCache:
http://hackage.haskell.org/package/TCache
TCache 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 is 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 This module defines an integrated way to interact with the user. `ask` isa 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.
This example show a widtget and a formlet togeter in a statically typed page defined in a declarative style. The server process has 10-15 lines. But it transparently manages user state, session timeout, shutdown the server process and restart it even after intended or unintended server shutdown.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | {-# OPTIONS -XDeriveDataTypeable -XMultiParamTypeClasses -XRecordWildCards #-} module Test where import MFlow.Hack.XHtml.All import Data.Typeable import Control.Monad.Trans import qualified Data.Vector as V import Data.TCache main= do userRegister "pepe" "pepe" 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 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 is a 2-tuple of Maybes shopProds :: V.Vector Int -> [Prod] -> View Html IO (Either Int Prod) shopProds cart products= br <++ -- add Html to the first widget 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..]]}) <+> -- operator to mix two wdigets br <++ -- add Html to the second widget p << "---Add a new product---" <++ table <<< -- <<< encloses a widget in HTML tags (tr <<< td ! [valign "top"] <<< widget (Form (Nothing :: Maybe Prod) ) ++> -- append Html after the widget 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 session so after this, the -- user will see the list or prudicts erased. -- In a real application the product list should be stored out of the session -- using TCache's writeDBRef for example -- The state is user specific. mainProds :: FlowM Html (Workflow IO) () mainProds = do setTimeouts (10*60) (24*60*60) -- setHeader $ \w -> bold << "Please enter user/password (pepe/pepe)" +++ br +++ w -- us <- getUser 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 |