packages feed

Spock 0.2.0.0 → 0.3.0.0

raw patch · 5 files changed

+113/−26 lines, 5 files

Files

Spock.cabal view
@@ -1,5 +1,5 @@ name:                Spock-version:             0.2.0.0+version:             0.3.0.0 synopsis:            Another Haskell web toolkit based on scotty description:         This toolbox provides everything you need to get a quick start into web hacking with haskell: sessions, database helper, authentication and the power of scotty Homepage:            https://github.com/agrafix/Spock@@ -16,7 +16,8 @@   exposed-modules:  Web.Spock   other-modules:       Web.Spock.SessionManager,                        Web.Spock.Monad,-                       Web.Spock.Cookie+                       Web.Spock.Cookie,+                       Web.Spock.Types   build-depends:       base >= 4 && < 5,                        scotty >= 0.5,                        wai ==1.4.*,
Web/Spock.hs view
@@ -4,12 +4,20 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} module Web.Spock-    ( -- * Spock's core functions, types and helpers-      spock, authed, runQuery, getState, Http.StdMethod (..), SpockM, SpockAction-    , authedUser, unauthCurrent, StorageLayer (..), PoolCfg (..)-    , setCookie, getCookie-      -- * Reexports from scotty-    , middleware, get, post, put, delete, patch, addroute, matchAny, notFound+    ( -- * Spock's core+      spock, SpockM, SpockAction+      -- * Database+    , runQuery, PoolOrConn (..), ConnBuilder (..), PoolCfg (..)+      -- * Routing+    , authed, get, post, put, delete, patch, addroute, Http.StdMethod (..)+      -- * Cookies+    , setCookie, setCookie', getCookie+      -- * Sessions+    , authedUser, unauthCurrent+      -- * State+    , getState+      -- * Other reexports from scotty+    , middleware, matchAny, notFound     , request, reqHeader, body, param, params, jsonData, files     , status, addHeader, setHeader, redirect     , text, html, file, json, source, raw@@ -27,26 +35,24 @@ import Control.Monad.Trans.Reader import Control.Monad.Trans.Resource import Data.Pool-import Data.Time.Clock import Web.Scotty.Trans import qualified Data.Text as T import qualified Network.HTTP.Types as Http -data PoolCfg-   = PoolCfg-   { pc_stripeCount :: Int-   , pc_keepOpenSec :: NominalDiffTime-   , pc_resPerStripe :: Int-   }-   deriving (Show, Eq)---- | Run a spock application using the warp server, a given db storageLayer and an initial state-spock :: Int -> PoolCfg -> StorageLayer conn -> st -> SpockM conn sess st () -> IO ()-spock port pc storageLayer initialState defs =+-- | Run a spock application using the warp server, a given db storageLayer and an initial state.+-- Spock works with database libraries that already implement connection pooling and+-- with those that don't come with it out of the box. For more see the 'PoolOrConn' type.+spock :: Int -> PoolOrConn conn -> st -> SpockM conn sess st () -> IO ()+spock port poolOrConn initialState defs =     do sessionMgr <- openSessionManager-       connectionPool <- createPool (sl_createConn storageLayer)-                         (sl_closeConn storageLayer) (pc_stripeCount pc)-                         (pc_keepOpenSec pc) (pc_resPerStripe pc)+       connectionPool <- case poolOrConn of+                           PCPool p ->+                               return p+                           PCConn cb ->+                               let pc = cb_poolConfiguration cb+                               in createPool (cb_createConn cb) (cb_destroyConn cb)+                                      (pc_stripes pc) (pc_keepOpenTime pc)+                                      (pc_resPerStripe pc)        let internalState =                WebState                { web_dbConn = connectionPool
Web/Spock/Cookie.hs view
@@ -11,6 +11,7 @@ import qualified Data.Text.Lazy as TL import qualified Network.Wai as Wai +-- | Read a cookie previously set in the users browser for your site getCookie :: MonadIO m => T.Text -> ActionT m (Maybe T.Text) getCookie name =     do req <- request@@ -21,11 +22,13 @@       parseCookies = map parseCookie . T.splitOn ";" . T.concat . T.words       parseCookie = first T.init . T.breakOnEnd "=" +-- | Set a cookie living for a given number of seconds setCookie :: MonadIO m => T.Text -> T.Text -> NominalDiffTime -> ActionT m () setCookie name value validSeconds =     do now <- liftIO getCurrentTime        setCookie' name value (validSeconds `addUTCTime` now) +-- | Set a cookie living until a specific 'UTCTime' setCookie' :: MonadIO m => T.Text -> T.Text -> UTCTime -> ActionT m () setCookie' name value validUntil =     let formattedTime =
Web/Spock/Monad.hs view
@@ -2,12 +2,10 @@ module Web.Spock.Monad where  import Web.Spock.Types-import Web.Spock.SessionManager  import Control.Applicative import Control.Monad import Control.Monad.Reader-import Control.Monad.Trans.Resource import Data.Pool import Data.Time.Clock ( UTCTime(..) ) import Web.Scotty.Trans@@ -20,12 +18,17 @@ webM :: MonadTrans t => WebStateM conn sess st a -> t (WebStateM conn sess st) a webM = lift -runQuery :: MonadTrans t => (conn -> IO a) -> t (WebStateM conn sess st) a+-- | Give you access to a database connectin from the connection pool. The connection is+-- released back to the pool once the function terminates.+runQuery :: MonadTrans t+         => (conn -> IO a) -> t (WebStateM conn sess st) a runQuery query =     webM $     do pool <- asks web_dbConn        liftIO $ withResource pool query +-- | Read the application's state. If you wish to have immutable state, you could+-- use a 'TVar' from the STM packge. getState :: MonadTrans t => t (WebStateM conn sess st) st getState = webM $ asks web_state 
+ Web/Spock/Types.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+module Web.Spock.Types where++import Web.Scotty.Trans++import Control.Applicative+import Control.Concurrent.STM+import Control.Monad.Reader+import Control.Monad.Trans.Resource+import Data.Pool+import Data.Time.Clock ( UTCTime(..), NominalDiffTime(..) )+import qualified Data.HashMap.Strict as HM+import qualified Data.Text as T++-- | Spock is supercharged Scotty, that's why the 'SpockM' is built on the+-- ScottyT monad. Insive the SpockM monad, you may define routes and middleware.+type SpockM conn sess st a = ScottyT (WebStateM conn sess st) a++-- | The SpockAction is the monad of all route-actions. You have access+-- to the database, session and state of your application.+type SpockAction conn sess st a = ActionT (WebStateM conn sess st) a++-- | If Spock should take care of connection pooling, you need to configure+-- it depending on what you need.+data PoolCfg+   = PoolCfg+   { pc_stripes :: Int+   , pc_resPerStripe :: Int+   , pc_keepOpenTime :: NominalDiffTime+   }++-- | The ConnBuilder instructs Spock how to create or close a database connection.+data ConnBuilder a+   = ConnBuilder+   { cb_createConn :: IO a+   , cb_destroyConn :: a -> IO ()+   , cb_poolConfiguration :: PoolCfg+   }++-- | You can feed Spock with either a connection pool, or instructions on how to build+-- a connection pool. See 'ConnBuilder'+data PoolOrConn a+   = PCPool (Pool a)+   | PCConn (ConnBuilder a)++data WebState conn sess st+   = WebState+   { web_dbConn :: Pool conn+   , web_sessionMgr :: SessionManager sess+   , web_state :: st+   }++newtype WebStateM conn sess st a = WebStateM { runWebStateM :: ReaderT (WebState conn sess st) (ResourceT IO) a }+    deriving (Monad, Functor, Applicative, MonadIO, MonadReader (WebState conn sess st))++type SessionId = T.Text+data Session a+    = Session+    { sess_id :: SessionId+    , sess_validUntil :: UTCTime+    , sess_data :: a+    }+type UserSessions a = TVar (HM.HashMap SessionId (Session a))++data SessionManager a+   = SessionManager+   { sm_loadSession :: SessionId -> IO (Maybe (Session a))+   , sm_sessionFromCookie :: MonadIO m => ActionT m (Maybe (Session a))+   , sm_createCookieSession :: MonadIO m => a -> ActionT m ()+   , sm_newSession :: a -> IO (Session a)+   , sm_deleteSession :: SessionId -> IO ()+   }