MFlow  Create and maintain dynamic Web applications as easy and fast as console applications
Thou shall not write request handlers
 This release: 11/06/2014.  agocorona@gmail.com (@agocorona) <>< . issues & bugs .  Mflow Source code,     source code of this site




A persistent and stateful flow: A shopping cart


In the example below, the shopping cart is stored as session data. In the ask statement that show the shopping cart,
getSessionData is used in the View monad (in bold), so when backtracking  the code will get the last shopping cart, so no roll-back effect will appear and the back button can be used for navigation purposes.  Here is a  loop with an alternance between buying from the catalog and showing the shopping cart. The loop, and the navigation can be executed forward, by pressing the links, and backward, by means of the back button. The result is the same.

If getSessionData is not in the View monad, and the cart variable in scope is used, then this variable will keep its value that it had at each moment of the loop, so when going back we will see fewer and fewer items in the shopping cart.

Here step is used, so the state is persistent. That means that even if the server shut down, the state will be recovered.  given the parameters setTimeouts will kill the process after 200 seconds. After one hour, if the user has not returned, the state will be erased and the shopping  cart will be empty next time. 
"

Running example

(in the light red box):

A persistent flow (uses step). The process is killed after the timeout set by setTimeouts but it is restarted automatically. Event If you restart the whole server, it remember the shopping cart The shopping cart is not logged, just the products bought returned by step are logged. The shopping cart is rebuild from the events (that is an example of event sourcing. .Defines a table with links that return ints and a link to the menu, that abandon this flow. .The cart state is not stored, Only the history of events is saved. The cart is recreated by running the history of events.

The second parameter of "setTimeout" is the time during which the cart is recorded

choose an item
itemtimes chosen
home
iphone0
ipod0
ipad0
home


Source code:

{-# OPTIONS  -XDeriveDataTypeable -XCPP #-}
module ShopCart ( shopCart) where
import Data.Typeable
import qualified Data.Vector as V
import Text.Blaze.Html5 as El
import Text.Blaze.Html5.Attributes as At hiding (step)
import Data.Monoid
import Data.String
import Data.Typeable
-- #define ALONE -- to execute it alone, uncomment this
#ifdef ALONE
import MFlow.Wai.Blaze.Html.All
main= runNavigation "" $ transientNav grid
#else
import MFlow.Wai.Blaze.Html.All hiding(retry, page)
import Menu
#endif

data ShopOptions= IPhone | IPod | IPad deriving (Bounded, Enum, Show,Read , Typeable)

newtype Cart= Cart (V.Vector Int) deriving Typeable

emptyCart= Cart $ V.fromList [0,0,0]

shopCart= shopCart1

shopCart1  =  do
--     setHeader  stdheader 
--     setTimeouts 200 $ 60*60   
     prod <-
        step . page $ do
             Cart cart <- getSessionData `onNothing` return  emptyCart

             moreexplain
              ++> 
              (table ! At.style (attr "border:1;width:20%;margin-left:auto;margin-right:auto")
              <<< caption <<  "choose an item"
              ++> thead << tr << ( th << b <<   "item" <> th << b <<  "times chosen")
              ++> (tbody
                  <<< tr ! rowspan (attr "2") << td << linkHome
                  ++> (tr <<< td <<< wlink  IPhone (b <<  "iphone")
                          <++  tdc << ( b <<  show ( cart V.! 0))
                  <|>  tr <<< td <<< wlink  IPod   (b <<  "ipod")
                          <++  tdc << ( b <<  show ( cart V.! 1))
                  <|>  tr <<< td <<< wlink  IPad   (b <<  "ipad")
                          <++  tdc << ( b <<  show ( cart V.! 2)))
                  <++  tr << td <<  linkHome
                  ))

                  
     let i = fromEnum prod
     Cart cart <- getSessionData `onNothing` return  emptyCart
     setSessionData . Cart $ cart V.// [(i, cart V.!  i + 1 )]
     shopCart1

    where
    tdc= td ! At.style (attr "text-align:center")
    linkHome= a ! href  (attr $ "/" ++ noScript) << b <<  "home"
    attr= fromString
    moreexplain= do
     p $ El.span <<(
         "A persistent flow  (uses step). The process is killed after the timeout set by setTimeouts "++
         "but it is restarted automatically. Event If you restart the whole server, it remember the shopping cart "++
         " The shopping cart is not logged, just the products bought returned by step are logged. The shopping cart "++
         " is rebuild from the events (that is an example of event sourcing."++
         " .Defines a table with links that return ints and a link to the menu, that abandon this flow. "++
         " .The cart state is not stored, Only the history of events is saved. The cart is recreated by running the history of events.")

     p << "The second parameter of \"setTimeout\" is the time during which the cart is recorded"

comments powered by Disqus