sunroof-server (empty) → 0.2
raw patch · 5 files changed
+658/−0 lines, 5 filesdep +aesondep +attoparsecdep +basesetup-changed
Dependencies added: aeson, attoparsec, base, containers, data-default, filepath, kansas-comet, scotty, semigroups, stm, sunroof-compiler, tagged, text, time, transformers, unordered-containers, vector, vector-space, wai-middleware-static, warp
Files
- LICENSE +30/−0
- Language/Sunroof/Server.hs +575/−0
- README.md +2/−0
- Setup.hs +2/−0
- sunroof-server.cabal +49/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c)2012, The University of Kansas++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 Andy Gill 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.
+ Language/Sunroof/Server.hs view
@@ -0,0 +1,575 @@++{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeFamilies #-}++-- | The Sunroof server module provides infrastructure to use+-- Sunroof together with kansas-comet.+--+-- It supports setting up a simple server with 'sunroofServer'+-- and provides basic functions for serverside communication+-- with the connected website ('syncJS', 'asyncJS' and 'rsyncJS').+--+-- This module also provides the abstractions for 'Downlink'+-- and 'Uplink'. They represent directed channels for sending data+-- from the server to the website and the other way aroun.+-- The sent data is queued and operations block properly if there+-- is no data available.+module Language.Sunroof.Server+ (+ -- * Basic Comet Server+ syncJS+ , asyncJS+ , rsyncJS+ , SunroofResult(..)+ , SunroofEngine(..)+ , jsonToJS+ , sunroofServer+ , SunroofServerOptions(..)+ , SunroofApp+ , debugSunroofEngine+ -- * Downlink+ , Downlink+ , newDownlink+ , getDownlink+ , putDownlink+ -- * Uplink+ , Uplink+ , newUplink+ , getUplink+ , putUplink+ -- * Timing+ , Timings(..)+ , newTimings+ , resetTimings+ , getTimings+ ) where++import Data.Aeson.Types ( Value(..), Object, Array )+import Data.Attoparsec.Number ( Number(..) )+import Data.List ( intercalate )+import Data.Text ( unpack )+import Data.Proxy ( Proxy(..) )+import Data.Default ( Default(..) )+import Data.Semigroup+import Data.Time.Clock+import qualified Data.Vector as V+import qualified Data.HashMap.Strict as M+import System.FilePath((</>))++import Control.Monad.IO.Class ( liftIO )+import Control.Concurrent.STM++import Network.Wai.Handler.Warp ( Port, settingsPort )+import Network.Wai.Middleware.Static+import qualified Web.Scotty as SC+import Web.KansasComet+ ( send, connect+ , Document, Options+ , kCometPlugin )+import qualified Web.KansasComet as KC++import Language.Sunroof+import Language.Sunroof.JavaScript+ ( Expr+ , literal, showExpr+ , scopeForEffect )+import Language.Sunroof.Classes ( Uniq )+import Language.Sunroof.Compiler ( compileJS )++-- -------------------------------------------------------------+-- Communication and Compilation+-- -------------------------------------------------------------++-- | The 'SunroofEngine' provides the verbosity level and+-- kansas comet document to the 'SunroofApp'.+data SunroofEngine = SunroofEngine+ { cometDocument :: Document+ -- ^ The document comet uses to manage the connected website.+ , uVar :: TVar Uniq+ -- ^ Unique number supply for our engine+ , engineVerbose :: Int+ -- ^ @0@ for none, @1@ for initializations,+ -- @2@ for commands done and @3@ for a complete log.+ , compilerOpts :: CompilerOpts+ -- ^ The options used to setup the compiler.+ , timings :: Maybe (TVar (Timings NominalDiffTime))+ -- ^ Performance timings of the compiler and communication.+ }++-- | Generate one unique integer from the document.+docUniq :: SunroofEngine -> IO Int+docUniq = docUniqs 1++-- | Generate n unique integers from the document.+docUniqs :: Int -> SunroofEngine -> IO Int+docUniqs n doc = atomically $ do+ u <- readTVar (uVar doc)+ writeTVar (uVar doc) (u + n)+ return u++-- | The number of uniques allocated for the first try of a compilation.+compileUniqAlloc :: Uniq+compileUniqAlloc = 32++-- | Log the given message on the given level+sunroofLog :: SunroofEngine -> Int -> String -> IO ()+sunroofLog engine level msg =+ if (engineVerbose engine >= level)+ then do+ putStr "Sunroof> "+ putStrLn msg+ else return ()++-- | Log the compilation result and return it+compileLog :: SunroofEngine -> String -> IO ()+compileLog engine src = do+ sequence_ $ fmap (sunroofLog engine 3) $+ [ "Compiled:", src]+ return ()++-- | Compile js using unique variables each time.+compileRequestJS :: SunroofEngine -> JS t () -> IO String+compileRequestJS engine jsm = do+ -- Allocate a standard amount of uniq for compilation+ uq <- docUniqs compileUniqAlloc engine+ -- Compile+ (stmts, uq') <- compileJS (compilerOpts engine) uq return jsm+ -- Check if the allocated amount was sufficient+ let txt = showExpr False $ scopeForEffect stmts++ if (uq' < uq + compileUniqAlloc)+ -- It was sufficient we are finished+ then do compileLog engine txt+ return txt+ -- It wasn't sufficient+ else do+ -- Allocate all that are needed+ newUq <- docUniqs (uq' - uq) engine+ -- Compile again+ (stmts', _) <- compileJS (compilerOpts engine) newUq return jsm+ let txt' = showExpr False $ scopeForEffect stmts'+ compileLog engine txt'+ return txt'++-- | Executes the Javascript in the browser without waiting for a result.+asyncJS :: SunroofEngine -> JS t () -> IO ()+asyncJS engine jsm = do+ t0 <- getCurrentTime+ src <- compileRequestJS engine jsm+ addCompileTime engine t0++ t1 <- getCurrentTime+ send (cometDocument engine) src -- send it, and forget it+ addSendTime engine t1+ return ()++-- | Executes the Javascript in the browser and waits for the result value.+-- The result value is given the corresponding Haskell type,+-- if possible (see 'SunroofResult').+syncJS :: forall a t . (SunroofResult a) => SunroofEngine -> JS t a -> IO (ResultOf a)+syncJS engine jsm | typeOf (Proxy :: Proxy a) == Unit = do+ _ <- syncJS engine (jsm >> return (0 :: JSNumber))+ return $ jsonToValue (Proxy :: Proxy a) Null+syncJS engine jsm = do+ up <- newUplink engine+ t0 <- getCurrentTime+ src <- compileRequestJS engine $ do+ v <- jsm+ up # putUplink v+ addCompileTime engine t0+ t1 <- getCurrentTime+ send (cometDocument engine) src+ addSendTime engine t1+ t2 <- getCurrentTime+ -- There is *no* race condition in here. If no-one is listening,+ -- then the numbered event gets queued up.+ r <- getUplink up+ addWaitTime engine t2+ return r++-- | Executes the Javascript in the browser and waits for the result.+-- The returned value is just a reference to the computed value.+-- This allows to precompile values like function in the browser.+rsyncJS :: forall a t . (Sunroof a) => SunroofEngine -> JS t a -> IO a+rsyncJS engine jsm = do+ uq <- docUniq engine -- uniq for the value+ let uq_lab = label ("remote_" <> cast (js uq))+ up :: Uplink JSNumber <- newUplink engine++ t0 <- getCurrentTime+ src <- compileRequestJS engine $ do+ v <- jsm+ -- Store the value inside the window object+ object "window" # uq_lab := v+ up # putUplink 0+ addCompileTime engine t0++ t1 <- getCurrentTime+ send (cometDocument engine) src+ addSendTime engine t1++ t2 <- getCurrentTime+ -- There is *no* race condition in here. If no-one is listening,+ -- then the numbered event gets queued up.+ _ <- getUplink up+ addWaitTime engine t2+ return $ object "window" ! uq_lab++-- -----------------------------------------------------------------------+-- Default Server Instance+-- -----------------------------------------------------------------------++-- | A comet application takes the engine/document we are currently communicating+-- with and delivers the IO action to be executed as server application.+type SunroofApp = SunroofEngine -> IO ()++-- | The 'SunroofServerOptions' specify the configuration of the+-- sunroof comet server infrastructure.+--+-- See 'sunroofServer' and 'SunroofServerOptions' for further information.+data SunroofServerOptions = SunroofServerOptions+ { cometPort :: Port+ -- ^ The port the server is reachable from.+ , cometResourceBaseDir :: FilePath+ -- ^ Will be used as base directory to search for all static files.+ -- Make this path absolute to run the server from anywhere.+ , cometIndexFile :: FilePath+ -- ^ The file to be used as index file (or landing page).+ -- This path is given relative to the 'cometResourceBaseDir'.+ , cometPolicy :: Policy+ -- ^ The default policy is to allow the @css@, @img@ and @js@+ -- folders to be used by the server, as well as the noDots policy.+ -- This policy can be overwritten to allow delivery of other files.+ , cometOptions :: Options+ -- ^ Provides the kansas comet options to use.+ -- Default options are provided with the 'Data.Default.def' instance.+ , sunroofVerbose :: Int+ -- ^ @0@ for none, @1@ for initializations,+ -- @2@ for commands done and @3@ for a complete log.+ , sunroofCompilerOpts :: CompilerOpts+ -- ^ The set of options to configure the Sunroof compiler.+ -- Default options are provided with the 'Data.Default.def' instance.+ }++-- | Sets up a comet server ready to use with sunroof.+--+-- @sunroofServer opts app@:+-- The @opts@ give various configuration for the comet server.+-- See 'SunroofServerOptions' for further information on this.+-- The application to run is given by @app@. It takes the current+-- engine/document as parameter. The document is needed for calls to 'syncJS',+-- 'asyncJS' and 'rsyncJS'.+--+-- The server provides the kansas comet Javascript on the path+-- @js/kansas-comet.js@.+--+-- Since @kansas-comet.js@ is a JQuery plugin you have to also+-- load a decent version of @jquery.js@ (or @jquery.min.js@)+-- and also @jquery-json.js@. They are available at:+--+-- * <http://jquery.com/>+--+-- * <https://code.google.com/p/jquery-json/>+--+-- For the index file to setup the communication correctly with the comet+-- server it has to load the @kansas-comet.js@ after the JQuery code+-- inside the @head@ (assuming you placed the JQuery code under @js/@):+--+-- > <script type="text/javascript" src="js/jquery.js"></script>+-- > <script type="text/javascript" src="js/jquery-json.js"></script>+-- > <script type="text/javascript" src="js/kansas-comet.js"></script>+--+-- It also has to execute the following Javascript at the end of the+-- index file to initialize the communication:+--+-- > <script type="text/javascript">+-- > $(document).ready(function() {+-- > $.kc.connect("/ajax");+-- > });+-- > </script>+--+-- The string @/ajax@ has to be set to whatever the comet prefix+-- in the 'Options' provided by the 'SunroofServerOptions' is.+-- These snippits will work for the 'def' instance.+--+-- Additional debug information can be displayed in the browser when+-- adding the following element to the index file:+--+-- > <div id="debug-log"></div>+--+-- Look into the example folder to see all of this in action.+sunroofServer :: SunroofServerOptions -> SunroofApp -> IO ()+sunroofServer opts cometApp = do+ let warpSettings = (SC.settings def) { settingsPort = cometPort opts }+ -- Be quiet scotty! ... and beam me up!+ let scottyOptions = def { SC.verbose = 0+ , SC.settings = warpSettings }+ SC.scottyOpts scottyOptions $ do+ kcomet <- liftIO kCometPlugin+ let rootFile = cometResourceBaseDir opts </> cometIndexFile opts+ let custom_policy = cometPolicy opts+ let pol = only [("", rootFile)+ ,("js/kansas-comet.js", kcomet)]+ <|> (custom_policy+ >-> addBase (cometResourceBaseDir opts))+ SC.middleware $ staticPolicy pol+ connect (cometOptions opts) $ wrapDocument opts cometApp++-- | Wrap the document into the sunroof engine.+wrapDocument :: SunroofServerOptions -> SunroofApp -> (Document -> IO ())+wrapDocument opts cometApp doc = do+ uqVar <- atomically $ newTVar 0+ cometApp $ SunroofEngine+ { cometDocument = doc+ , uVar = uqVar+ , engineVerbose = sunroofVerbose opts+ , compilerOpts = sunroofCompilerOpts opts+ , timings = Nothing+ }++-- | Default options to use for the sunroof comet server.+--+-- [@cometPort@] Default port is @3000@.+--+-- [@cometResourceBaseDir@] Default resource location is @"."@.+--+-- [@cometIndexFile@] Default index file is @"index.html"@.+--+-- [@cometOptions@] Uses the server path @/ajax@ for the+-- comet JSON communication. Sets verbosity to @0@ (quiet).+--+-- [@sunroofVerbose@] Is set to @0@ (quiet).+--+defaultServerOpts :: SunroofServerOptions+defaultServerOpts = SunroofServerOptions+ { cometPort = 3000+ , cometResourceBaseDir = "."+ , cometIndexFile = "index.html"+ , cometPolicy = defaultPolicy+ , cometOptions = def { KC.prefix = "/ajax", KC.verbose = 0 }+ , sunroofVerbose = 0+ , sunroofCompilerOpts = def+ }++defaultPolicy :: Policy+defaultPolicy = noDots >-> (hasPrefix "css/" <|>+ hasPrefix "js/" <|>+ hasPrefix "img/")++-- | The 'defaultServerOpts'.+instance Default SunroofServerOptions where+ def = defaultServerOpts++-- -------------------------------------------------------------+-- Downlink API+-- -------------------------------------------------------------++-- | 'Downlink's are an abstraction provided for sending+-- Javascript data from the server to the website.+-- The type parameter describes the elements+-- that are transmited through the downlink.+data Downlink a = Downlink SunroofEngine (JSChan a)++-- | Create a new downlink.+newDownlink :: forall a . (Sunroof a, SunroofArgument a)+ => SunroofEngine -> IO (Downlink a)+newDownlink eng = do+ chan <- rsyncJS eng (newChan :: JSA (JSChan a))+ return $ Downlink eng chan++-- | Send data to the website.+putDownlink :: (Sunroof a, SunroofArgument a)+ => Downlink a -> JSA a -> IO ()+putDownlink (Downlink eng chan) val = asyncJS eng $ do+ v <- val+ writeChan v chan++-- | Request data in the downlink. This may block until+-- data is available.+getDownlink :: (Sunroof a, SunroofArgument a)+ => Downlink a -> JSB a+getDownlink (Downlink _eng chan) = readChan chan++-- -------------------------------------------------------------+-- Uplink API+-- -------------------------------------------------------------++-- | 'Uplink's are an abstraction provided for sending+-- Javascript data from the website back to the server.+-- Only data that can be translated back to a Haskell+-- value can be sent back.+-- The type parameter describes the elements+-- that are transmited through the uplink.+data Uplink a = Uplink SunroofEngine Uniq++-- | Create a new uplink.+newUplink :: SunroofEngine -> IO (Uplink a)+newUplink eng = do+ u <- docUniq eng+ return $ Uplink eng u++-- | Send Javascript data back to the server.+putUplink :: (Sunroof a) => a -> Uplink a -> JS t ()+putUplink a (Uplink _ u) = kc_reply (js u) a++-- | Request data in the uplink. This may block until+-- data is available.+getUplink :: forall a . (SunroofResult a) => Uplink a -> IO (ResultOf a)+getUplink (Uplink eng u) = do+ val <- KC.getReply (cometDocument eng) u+ -- TODO: make this throw an exception if it goes wrong (I supose error does this already)+ return $ jsonToValue (Proxy :: Proxy a) val++-- -------------------------------------------------------------+-- Comet Javascript API+-- -------------------------------------------------------------++-- | Binding for the Javascript function to send replies to the+-- server.+kc_reply :: (Sunroof a) => JSNumber -> a -> JS t ()+kc_reply n a = fun "$.kc.reply" `apply` (n,a)++-- -----------------------------------------------------------------------+-- JSON Value to Haskell/Sunroof conversion+-- -----------------------------------------------------------------------++-- | Provides correspondant Haskell types for certain Sunroof types.+class (Sunroof a) => SunroofResult a where+ -- | The Haskell value type associated with this 'Sunroof' type.+ type ResultOf a+ -- | Converts the given JSON value to the corresponding+ -- Haskell value. A error is thrown if the JSON value can+ -- not be converted.+ jsonToValue :: Proxy a -> Value -> ResultOf a++-- | @null@ can be translated to @()@.+instance SunroofResult () where+ type ResultOf () = ()+ jsonToValue _ (Null) = ()+ jsonToValue _ v = error $ "jsonToValue: JSON value is not unit: " ++ show v++-- | 'JSBool' can be translated to 'Bool'.+instance SunroofResult JSBool where+ type ResultOf JSBool = Bool+ jsonToValue _ (Bool b) = b+ jsonToValue _ v = error $ "jsonToValue: JSON value is not a boolean: " ++ show v++-- | 'JSNumber' can be translated to 'Double'.+instance SunroofResult JSNumber where+ type ResultOf JSNumber = Double+ jsonToValue _ (Number (I i)) = fromInteger i+ jsonToValue _ (Number (D d)) = d+ jsonToValue _ v = error $ "jsonToValue: JSON value is not a number: " ++ show v++-- | 'JSString' can be translated to 'String'.+instance SunroofResult JSString where+ type ResultOf JSString = String+ jsonToValue _ (String s) = unpack s+ jsonToValue _ v = error $ "jsonToValue: JSON value is not a string: " ++ show v++-- | 'JSArray' can be translated to a list of the 'ResultOf' the values.+instance forall a . SunroofResult a => SunroofResult (JSArray a) where+ type ResultOf (JSArray a) = [ResultOf a]+ jsonToValue _ (Array ss) = map (jsonToValue (Proxy :: Proxy a)) $ V.toList ss+ jsonToValue _ v = error $ "jsonToValue: JSON value is not an array : " ++ show v++-- | Converts a JSON value to a Sunroof Javascript expression.+jsonToJS :: Value -> Expr+jsonToJS (Bool b) = unbox $ js b+jsonToJS (Number (I i)) = unbox $ js i+jsonToJS (Number (D d)) = unbox $ js d+jsonToJS (String s) = unbox $ js $ unpack s+jsonToJS (Null) = unbox $ nullJS+jsonToJS (Array arr) = jsonArrayToJS arr+jsonToJS (Object obj) = jsonObjectToJS obj++-- | Converts a JSON object to a Sunroof expression.+jsonObjectToJS :: Object -> Expr+jsonObjectToJS obj = literal $+ let literalMap = M.toList $ fmap (show . jsonToJS) obj+ convertKey k = "\"" ++ unpack k ++ "\""+ keyValues = fmap (\(k,v) -> convertKey k ++ ":" ++ v) literalMap+ in "{" ++ intercalate "," keyValues ++ "}"++-- | Converts a JSON array to a Sunroof expression.+jsonArrayToJS :: Array -> Expr+jsonArrayToJS arr = literal $+ "(new Array(" ++ (intercalate "," $ V.toList $ fmap (show . jsonToJS) arr) ++ "))"++{- Orphans:+instance SunroofValue Value where+ type ValueOf Value = JSObject+ js = box . jsonToJS++instance SunroofValue Text where+ type ValueOf Text = JSString+ js = string . unpack+-}++-- -------------------------------------------------------------+-- Debugging+-- -------------------------------------------------------------++-- | Setup a 'SunroofEngine' for debugging.+debugSunroofEngine :: IO SunroofEngine+debugSunroofEngine = do+ doc <- KC.debugDocument+ uqVar <- atomically $ newTVar 0+ return $ SunroofEngine doc uqVar 3 def Nothing++-- | Timings for communication and compilation.+data Timings a = Timings+ { compileTime :: !a -- ^ How long spent compiling.+ , sendTime :: !a -- ^ How long spent sending.+ , waitTime :: !a -- ^ How long spent waiting for a response.+ }+ deriving Show++-- | Apply a function by applying it to each timing.+instance Functor Timings where+ fmap f (Timings t1 t2 t3) = Timings (f t1) (f t2) (f t3)++-- | Combine timings by combining each single timing.+instance Semigroup a => Semigroup (Timings a) where+ (Timings t1 t2 t3) <> (Timings u1 u2 u3) = Timings (t1<>u1) (t2<>u2) (t3<>u3)++-- | Create timings in the 'SunroofEngine'.+newTimings :: SunroofEngine -> IO SunroofEngine+newTimings e = do+ v <- atomically $ newTVar $ Timings 0 0 0+ return $ e { timings = Just v }++-- | Reset all timings.+resetTimings :: SunroofEngine -> IO ()+resetTimings (SunroofEngine { timings = Nothing }) = return ()+resetTimings (SunroofEngine { timings = Just t }) = atomically $ writeTVar t $ Timings 0 0 0++-- | Get timings from the 'SunroofEngine'.+getTimings :: SunroofEngine -> IO (Timings NominalDiffTime)+getTimings (SunroofEngine { timings = Nothing }) = return $ Timings 0 0 0+getTimings (SunroofEngine { timings = Just t }) = atomically $ readTVar t++-- | Add a timing for compilation.+addCompileTime :: SunroofEngine -> UTCTime -> IO ()+addCompileTime (SunroofEngine { timings = Nothing }) _start = return ()+addCompileTime (SunroofEngine { timings = Just t }) start = do+ end <- getCurrentTime+ atomically $ modifyTVar t $ \ ts -> ts { compileTime = compileTime ts + diffUTCTime end start}+ return ()++-- | Add a timing for sending.+addSendTime :: SunroofEngine -> UTCTime -> IO ()+addSendTime (SunroofEngine { timings = Nothing }) _start = return ()+addSendTime (SunroofEngine { timings = Just t }) start = do+ end <- getCurrentTime+ atomically $ modifyTVar t $ \ ts -> ts { sendTime = sendTime ts + diffUTCTime end start}+ return ()++-- | Add a timing for waiting for a response.+addWaitTime :: SunroofEngine -> UTCTime -> IO ()+addWaitTime (SunroofEngine { timings = Nothing }) _start = return ()+addWaitTime (SunroofEngine { timings = Just t }) start = do+ end <- getCurrentTime+ atomically $ modifyTVar t $ \ ts -> ts { waitTime = waitTime ts + diffUTCTime end start}+ return ()
+ README.md view
@@ -0,0 +1,2 @@+sunroof-server+==============
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ sunroof-server.cabal view
@@ -0,0 +1,49 @@+Name: sunroof-server+Version: 0.2+Synopsis: Monadic Javascript Compiler - Server Utilities+Homepage: https://github.com/ku-fpg/sunroof-server+Bug-reports: https://github.com/ku-fpg/sunroof-server+License: BSD3+License-file: LICENSE+Author: Andrew Gill <andygill@ku.edu>, Jan Bracker <jbra@informatik.uni-kiel.de>+Maintainer: Jan Bracker <jbra@informatik.uni-kiel.de>+Copyright: (c) 2012 The University of Kansas+Category: Web, Language, Embedded, Javascript+Stability: experimental+Build-type: Simple+Cabal-version: >= 1.10+Description:+ Utilities to use Sunroof together with Kansas-Comet to create web applications.++Extra-source-files:+ README.md++Library+ Exposed-modules: Language.Sunroof.Server+ default-language: Haskell2010+ build-depends: aeson >= 0.5,+ attoparsec >= 0.8.6.1,+ vector >= 0.7.1,+ unordered-containers >= 0.1.3.0,+ base >= 4.3.1 && < 5,+ containers >= 0.4,+ data-default == 0.5.*,+ vector-space >= 0.8.6, + kansas-comet == 0.2,+ wai-middleware-static >= 0.3.1 && < 0.4,+ filepath >= 1.3 && < 1.4,+ scotty >= 0.4.2 && < 0.5,+ semigroups >= 0.9,+ warp >= 1.3.4.1,+ stm >= 2.2,+ transformers == 0.3.*,+ text >= 0.11,+ tagged >= 0.4.4,+ time >= 1.4,+ sunroof-compiler == 0.2.*++ GHC-options: -Wall++source-repository head+ type: git+ location: git@github.com:ku-fpg/sunroof-server.git