packages feed

WebCont (empty) → 0.0.0

raw patch · 4 files changed

+257/−0 lines, 4 filesdep +applicative-extrasdep +basedep +concatenativesetup-changed

Dependencies added: applicative-extras, base, concatenative, containers, formlets, happstack-server, happstack-state, happstack-util, mtl, utf8-string, xhtml

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c)2010, Sam Anklesaria++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Sam Anklesaria nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ WebCont.cabal view
@@ -0,0 +1,61 @@+Name:                WebCont++-- The package version. See the Haskell package versioning policy+-- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for+-- standards guiding when and how versions should be incremented.+Version:             0.0.0++-- A short (one-line) description of the package.+Synopsis:            Continuation based web programming for Happstack++-- A longer description of the package.+-- Description:         ++-- URL for the project homepage or repository.+Homepage:            http://patch-tag.com/r/salazar/webconts/snapshot/current/content/pretty++-- The license under which the package is released.+License:             BSD3++-- The file containing the license text.+License-file:        LICENSE++-- The package author(s).+Author:              Sam Anklesaria++-- An email address to which users can send suggestions, bug reports,+-- and patches.+Maintainer:          amsay@amsay.net++-- A copyright notice.+-- Copyright:           ++Category:            Web++Description:        WebCont allows continuation based web programming to be integrated with Happstack, allowing+                    step based interfaces along with traditional handlers. It is based on <http://gist.github.com/260052>++Build-type:          Simple++-- Extra files to be distributed with the package, such as examples or+-- a README.+-- Extra-source-files:  ++-- Constraint on the version of Cabal needed to build this package.+Cabal-version:       >=1.2+++Library+  -- Modules exported by the library.+  Exposed-modules:     WebCont+  +  -- Packages needed in order to build this package.+  Build-depends:       happstack-server >= 0.5, happstack-state >= 0.5, happstack-util >= 0.5, mtl, concatenative >= 1, formlets < 1,+                       utf8-string, containers, xhtml, applicative-extras, base >= 3 && < 5+  +  -- Modules not exported by this package.+  -- Other-modules:       +  +  -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.+  -- Build-tools:         +  
+ WebCont.hs view
@@ -0,0 +1,164 @@+{-# LANGUAGE TypeSynonymInstances #-}+{-| WebConts allow simple continuation based behavior in Happstack applications through the use of cookies+    and an IntMap on the server mapping cookie names to continuations. +    It is based on the paste <http://gist.github.com/260052>. For the arc challenge:++>    arc = do+>        name <- doform' frm+>        display $ samelink "click here"+>        display $ "you said" ++ name++    A more complicated example, creating a User datatype with a different page for each field:++>   data User = User {age:: Int, name::String, dead::Bool} deriving (Show)+>   makeUser = liftM3 User age name dead >>= (display . show) where+>       age = doform' $ label "Age: " *> frm+>       name = doform' $ label "Name: " *> frm+>       dead = doform' $ label "Dead?: " *> frm++    To run the continuations in Happstack, use 'runStateless' or 'runWithState', giving each continuation in a list+    as a parameter, depending on whether you want MACID support enabled:++>   runStateless nullConf [arc, makeUser] []++-}+module WebCont (+    doform, doform', runWithState, runStateless, frm, display, samelink,+    WebCont(..), Result(..), ContForm, DefaultForm, nullConf,+) where+import Happstack.Server.SimpleHTTP hiding (method)+import Happstack.State hiding (dup)+import Happstack.Util.Cron+import Control.Monad+import Control.Monad.Identity (Identity (..))+import Control.Monad.Trans+import Control.Monad.Reader+import Control.Concurrent+import Control.Exception hiding (catch)+import Control.Concatenative+import Control.Applicative+import Control.Applicative.Error (Failing (..))+import Text.Formlets+import Text.XHtml.Strict.Formlets hiding (label)+import Text.XHtml.Strict hiding (input, checkbox)+import qualified Data.IntMap as Map+import Data.IntMap (IntMap)+import Data.Monoid+import Data.ByteString.Lazy.UTF8 (toString)+import qualified Happstack.Server.SimpleHTTP as H (ContentType(..))+import qualified Text.Formlets as F (ContentType(..))++data Result r a = Done a | Via r (WebCont r a) deriving Show++-- | A WebCont is a function from 'Env' to a value of a,+--   displaying a response of r. Both r and a must be+--   Html for runnable continuations.+newtype WebCont r a = WebCont {runWeb :: Env -> Result r a} deriving Show++runWeb' :: Env -> (MVar Int, MVar ContTable) -> WebCont Html () -> ServerPartT IO Response+runWeb' e t w = case runWeb w e of+    Done _ -> expireCookie "cont" >> mzero+    Via r w' -> serializeCont t w' >> ok (toResponse r)++type ContForm a = XHtmlForm Identity a++instance Monad (WebCont r) where+    return = WebCont . const . Done+    w >>= f = WebCont $ \e -> case (runWeb w e) of+        Done a -> runWeb (f a) e+        Via r w' -> Via r (w' >>= f)++instance (Monoid r, Monoid a) => Monoid (WebCont r a) where+    (WebCont f) `mappend` (WebCont g) = WebCont $ f &&. g >>@ c where+        (Done _) `c` x@(Via _ _) = x+        x@(Via _ _) `c` (Done _) = x+        (Via v a) `c` (Via t b) = Via (v `mappend` t) (a `mappend` b) -- this instance seems flawed+    mempty = WebCont . const . Done $ mempty++-- | Lift a form into the WebCont monad using a function+---  to modify its html, and a function to display errors+doform :: (Html -> r) -> ([String] -> Html -> r) -> ContForm a -> WebCont r a+doform l err f = WebCont . const $+    Via (l (createForm [] f)) succPage where+        succPage = WebCont $ dup >>. first parse >>@ formResult+        parse env = x where (Identity x, _, _) = runFormState env f+        formResult (Success a) _ = Done a+        formResult (Failure faults) e = Via (err faults (createForm e f)) succPage+        makeForm frm = form ! [method "POST"] << (frm +++ submit "submit" "submit")++createForm :: Env -> ContForm a -> Html+createForm env frm = form ! [method "POST"] << (xml +++ submit "submit" "Submit") where+  (extractor, xml, endState) = runFormState env frm++-- | Lift a form into the WebCont monad without embellishment, displaying errors inline+doform' :: ContForm a -> WebCont Html a+doform' = doform id $ curry (unlines **. id >>@ (+++))++-- | Display a page, ignoring the value+display :: HTML a => a -> WebCont Html ()+display h = WebCont $ const $ Via (toHtml h) (return ())++-- | Links to the same page, leading to the next step in a continuation+samelink :: HTML a => a -> HotLink+samelink a = hotlink "" (toHtml a)++type ContTable = IntMap (WebCont Html ())++runConts :: (MVar Int, MVar ContTable) -> ServerPartT IO Response+runConts t = withDataFn (lookCookieValue "cont") $ \c-> do+    w <- liftIO $ deserializeCont t c+    case w of+        Just w' -> do+            d <- lookInputPairs+            clearCont t c+            runWeb' (maybe [] id d) t w'+        Nothing -> mzero++lookInputPairs :: ServerPartT IO (Maybe Env)+lookInputPairs = getDataFn $ asks fst >>= (return . map f) where+    f (k,Input v Nothing _) = (k,Left (toString v))+    f (k,Input v (Just f) (H.ContentType t s p)) = (k,Right (File v f (F.ContentType t s p)))++deserializeCont :: (MVar Int, MVar ContTable) -> String -> IO (Maybe (WebCont Html ()))+deserializeCont (_,t) s = do+    e <- readMVar t+    let p = Map.lookup (read s :: Int) e+    return p++clearCont :: (MVar Int, MVar ContTable) -> String -> ServerPartT IO ()+clearCont (_,t) s = liftIO (takeMVar t >>= (putMVar t . Map.delete (read s)))++serializeCont :: (MVar Int, MVar ContTable) -> (WebCont Html ()) -> ServerPartT IO ()+serializeCont (i,t) f = do+    k <- liftIO $ takeMVar i+    liftIO $ putMVar i (k+1)+    liftIO $ takeMVar t >>= (putMVar t . Map.insert k f)+    addCookie 600 (mkCookie "cont" (show k))++-- | Entry point for the continuation server: sets up continuation table, MACID state,+--   and a chron job to add a checkpoint daily+runWithState :: (Methods a, Component a) => Conf -> Proxy a -> [WebCont Html ()] -> [ServerPartT IO Response] -> IO ()+runWithState c p conts controller =+    bracket (startSystemState p) (biM_ createCheckpoint shutdownSystem) $ \control->+        do withThread (runStateless c conts controller) $ do+           withThread (cron 86400 (createCheckpoint control)) $ waitForTermination+        where withThread init action = bracket (forkIO $ init) (killThread) (const action)++-- | Entry point for the continuation server: starts the server without MACID support+runStateless :: Conf -> [WebCont Html ()] -> [ServerPartT IO Response] -> IO ()+runStateless c conts controller = do+    e <- newMVar Map.empty+    counter <- newMVar (0 :: Int)+    let env = (counter, e)+    simpleHTTP c (runConts env `mplus` msum (map (runWeb' [] env) conts) `mplus` msum controller)++-- | Defines a normal form style to use for a given type+class DefaultForm i where frm :: Form Html Identity i+instance DefaultForm String where frm = input Nothing+instance DefaultForm Integer where frm = inputInteger Nothing+instance DefaultForm Int where frm = fromInteger <$> (inputInteger Nothing)+instance DefaultForm Bool where frm = checkbox Nothing+instance (DefaultForm a, DefaultForm b) => DefaultForm (a,b) where +  frm = (,) <$> frm <*> frm+instance HTML Integer where toHtml = toHtml . show+instance Applicative Identity where pure = return; (<*>) = ap;