hvac-0.1: Http view and controller. A transactional, declarative framework for lightweight web applications.ContentsIndex
Network.Frameworks.HVAC
Portabilityportable
Stabilityexperimental
Maintainers.clover@gmail.com
Contents
Types
Controller Combinators
Cache Combinators
File Actions
Session Actions
Database Actions
STM Actions
Lifted STM consistency primitives
Errors and Messages
Errors and Messages (Experimental)
Running HVAC
CGI Methods
lifting from STM
Description

HTTP View And Controller (hvac): A lightweight atomic, applicative, transactional haskell web framework library, for use with the HDBC database library and HStringTemplate templating library. Emphasis is placed on correctness of transactional logic, and brevity of code.

Currently very new and unstable. Cache combinators particularly so. See included files for examples of use.

Templates output via the renderf function will have the following attributes set automatically: scriptURI, requestURL, refererURL, request (i.e. all parameters set via GET or POST), errors (generated through form validation), messages (set explicitly), session.

Note that binaries must be compiled with -threaded in order to function properly. Additionally, if you use the caching functions, you must compile your binaries with -O2 to ensure the caching works.

Along with the functions listed here, all functions in Network.CGI are available and exported or reimplemented aside from those dealing with exceptions where HVAC promotes a different model (i.e. handleErrors, tryCGI and throwCGI). catchCGI is provided however, and may prove to be of infrequent use.

Synopsis
data HCGI q s a
data ReadOnly
data ReadWrite
data HVDB = forall conn . IConnection conn => HVDB conn
type ValidationFunc s a b = a -> ErrorT String (HCGI ReadOnly s) b
h :: HCGI q s ()
(|/) :: (MonadState (HVPar a1) m, Alternative m) => m a -> String -> m ()
(|//) :: (MonadCGI m, Alternative m) => m a -> String -> m ()
(|?) :: (MonadCGI m, Alternative m) => m a -> (String, String) -> m ()
(|\) :: (Read x, MonadState (HVPar s) m, Alternative m) => m a -> (x -> m b) -> m b
(|\\) :: (MonadState (HVPar s) m, Alternative m) => m a -> (String -> m b) -> m b
(|.) :: (MonadState (HVPar s) m, Alternative m) => m a -> m b -> m b
cacheHCGI :: HCGI ReadOnly s a -> HCGI ReadWrite s a
cacheHCGI' :: Int -> HCGI ReadOnly s a -> HCGI ReadWrite s a
cacheHCGI'' :: Int -> Int -> HCGI ReadOnly s a -> HCGI ReadWrite s a
cacheHCGINoisy :: HCGI ReadOnly s a -> HCGI ReadWrite s a
hvReadFile :: (MonadState (HVPar a) m, Functor m, MonadSTM m) => FilePath -> m String
hvWriteFile :: FilePath -> String -> HCGI ReadWrite s ()
hvAppendFile :: FilePath -> String -> HCGI ReadWrite s ()
hvWriteUniqueFile :: FilePath -> String -> HCGI ReadWrite s FilePath
getSes :: (MonadState (HVPar a) m, MonadSTM m) => m (Maybe a)
putSes :: Maybe s -> HCGI ReadWrite s ()
modifySes :: (Maybe s -> (Maybe s, b)) -> HCGI ReadWrite s b
select :: (MonadState (HVPar p) m, MonadSTM m, Functor m, MapFromTuple a SqlValue) => [Char] -> a -> m [Map String SqlValue]
selectRow :: (MonadState (HVPar p) m, MonadSTM m, Functor m, MapFromTuple a SqlValue) => [Char] -> a -> m (Map String SqlValue)
selectVal :: (MonadState (HVPar p) m, MonadSTM m, Functor m, MapFromTuple a SqlValue) => [Char] -> a -> m SqlValue
execStat :: MapFromTuple a SqlValue => String -> a -> HCGI ReadWrite s Integer
hvReadTVar :: MonadSTM m => TVar a -> m a
hvWriteTVar :: TVar a -> a -> HCGI ReadWrite s ()
hvModifyTVar :: TVar a -> (a -> (a, b)) -> HCGI ReadWrite s b
gAlwaysSucceeds :: MonadSTM m => STM a -> m ()
gAlways :: MonadSTM m => STM Bool -> m ()
gRetry :: MonadSTM m => m a
gCheck :: MonadSTM m => Bool -> m a
addErrors :: [(String, String)] -> HCGI q s ()
addMsg :: String -> HCGI q s ()
handleValidation :: [(String, ValidationFunc s String a)] -> [(String, String)] -> HCGI q s CGIResult -> [a] -> HCGI q s CGIResult -> HCGI q s CGIResult
withValidation :: [(String, ValidationFunc s String a)] -> [a] -> HCGI q s CGIResult -> HCGI q s CGIResult
handleValidation' :: (MapFromTuple a (String, ValidationFunc s String Dynamic), MapToTuple a1 Dynamic, TupleMatchTwo a a1) => a -> ([(String, String)] -> HCGI r s t) -> (a1 -> HCGI r s t) -> HCGI r s t
withValidation' :: (MapFromTuple a (String, ValidationFunc s String Dynamic), MapToTuple a1 Dynamic, TupleMatchTwo a a1) => a -> (a1 -> HCGI r s t) -> HCGI r s t
newtype Box a = Box a
data HVConf a = HVConf {
threads :: Int
dbConn :: IO HVDB
sessTimeout :: Int
sessPoll :: Int
sessPersist :: a -> IO ()
cacheTime :: Int
}
hvConf :: HVConf a
runHVAC :: HVConf s -> HCGI q s CGIResult -> IO ()
continue :: Alternative f => f a
requestURL :: MonadCGI m => m String
refererURL :: MonadCGI m => m String
pathInfo :: MonadCGI m => m String
logHVAC :: MonadSTM m => String -> m ()
outputException :: (MonadCGI m, MonadSTM m, Functor m) => Exception -> m (CGIResult)
outputError :: (MonadCGI m, MonadSTM m, Functor m) => Int -> String -> [String] -> m (CGIResult)
catchCGI :: HCGI q s a -> (Exception -> HCGI q s a) -> HCGI q s a
outputNotFound :: (MonadSTM m, MonadCGI m, Functor m) => String -> m (CGIResult)
outputMethodNotAllowed :: (MonadSTM m, MonadCGI m, Functor m) => [String] -> m (CGIResult)
outputInternalServerError :: (MonadSTM m, MonadCGI m, Functor m) => [String] -> m (CGIResult)
stmToHCGI :: STM a -> HCGI ReadWrite s a
stmToReadOnlyHCGI :: STM a -> HCGI ReadOnly s a
Types
data HCGI q s a
Monad for all HVAC actions. Parameterized by a tag indicating if it modifies state and by the type of its session. Note that it is an instance of Applicative and Alternative, and HCGI actions are intended to be composed via use of <|>. Further, note that if an action does not return a CGIResult but rather "fails" via a call to continue, modifications to state conducted by the action will persist. However, if an entire chain of HCGI actions concludes without a result, a 404 will be returned and all modifications to state will be rolled back. Furthermore, if an action throws an exception, the entire request will fail, and all modifications to state will roll back. If an action dies due to a call to retry, all modifications to state will be rolled back, and the action will indeed be retried.
show/hide Instances
MonadError Exception (HCGI q s)
ToSElem s => SEType ByteString (HCGI q s CGIResult)
MonadState (HVPar s) (HCGI q s)
ToSElem s => SEType ([] Char) (HCGI q s CGIResult)
Monad (HCGI q s)
Functor (HCGI q s)
CaughtMonadSTM (HCGI q s)
MonadCGI (HCGI q s)
MonadSTM (HCGI q s)
Applicative (HCGI q s)
Alternative (HCGI q s)
MonadDB (HCGI q s) HVDB
data ReadOnly
data ReadWrite
data HVDB
HVDB is an existential type used to wrap HDBC database connections. This code is borrowed wholesale from HDBC and only is used currently because ConnWrapper is not exported properly from HDBC.
Constructors
forall conn . IConnection conn => HVDB conn
show/hide Instances
IConnection HVDB
MonadDB (HCGI q s) HVDB
type ValidationFunc s a b = a -> ErrorT String (HCGI ReadOnly s) b
A type synonym for validation functions. Parameterized by input type and output type. Not that it is *not* parameterized by session type, and thus that session functions are unavailable. See Network.HVAC.Validators for more detail.
Controller Combinators
h :: HCGI q s ()
A null HCGI action, used to begin a string of path combinators.
(|/) :: (MonadState (HVPar a1) m, Alternative m) => m a -> String -> m ()
Combinator that filters on and consumes the next element of the url path. h |/ "dir" |/ "subdir" will match "/dir/subdir". Consumption of the path element backtracks on failure.
(|//) :: (MonadCGI m, Alternative m) => m a -> String -> m ()
Combinator that filters on the request method. h |// "GET" will match requests made using get.
(|?) :: (MonadCGI m, Alternative m) => m a -> (String, String) -> m ()
Combinator that filters on any parameter (via put or get). h |? ("cmd","foo") will match on ?cmd=foo
(|\) :: (Read x, MonadState (HVPar s) m, Alternative m) => m a -> (x -> m b) -> m b
Combinator that matches and consumes the next element of the path if that element can be successfully read as the proper type and passed to the following lambda expression. h |\ \x -> output (x + (1.5::Float)) will match on "/12" and output "13.5". Consumption of the path element backtracks on failure.
(|\\) :: (MonadState (HVPar s) m, Alternative m) => m a -> (String -> m b) -> m b
Combinator that consumes the next element of the path and passes it as an unparsed string into the following lambda expression. h |\\ \x -> output (x++"99") will match on "/12" and output "1299" Consumption of the path element backtracks on failure.
(|.) :: (MonadState (HVPar s) m, Alternative m) => m a -> m b -> m b
Combinator that only matches if the remaining path is empty.
Cache Combinators
cacheHCGI :: HCGI ReadOnly s a -> HCGI ReadWrite s a

Caches a HCGI ReadOnly action, for the default span of time specified in the parameters to runHVAC. The cache is based on the url. Note: Caching uses unsafePerformIO to conjure up a map. As such, it may not work as intended if optimization (i.e. -O2) is not enabled to ensure proper let-floating. Use cacheHCGINoisy if you suspect an issue.

Additionally, data that expires from the cache is not cleaned from it. Thus, potentially, memory usage can grow without bounds. Use cacheHCGI'' if this is an issue. Finally, note that all cache combinators are currently based in the STM monad. This is potentially very cool, and potentially extremely silly -- the jury is still out on this experimental design choice.

cacheHCGI' :: Int -> HCGI ReadOnly s a -> HCGI ReadWrite s a
As with cacheHCGI but takes an explicit timeout, in seconds.
cacheHCGI'' :: Int -> Int -> HCGI ReadOnly s a -> HCGI ReadWrite s a
As with cacheHCGI' but takes an explicit maximum limit of values to cache as an additional parameter. Values will be dropped in FIFO order.
cacheHCGINoisy :: HCGI ReadOnly s a -> HCGI ReadWrite s a
As with cacheHCGI but writes to stderr every time a new cache is created. Use for tracking down issues with insufficient let floating.
File Actions
hvReadFile :: (MonadState (HVPar a) m, Functor m, MonadSTM m) => FilePath -> m String
Reads a file in the HVAC Monad. Strict, atomic. Guarantees that if you read a file and then later write to it, the file as written will be unchanged from the file as read. HVAC file operation concurrency is optimistic on reads and pessemistic on writes. Many readers can access a file at once and one writer can secure a lock. Should a reader discover that a write lock has been obtained over the course of its transaction, the transaction will be rolled back.
hvWriteFile :: FilePath -> String -> HCGI ReadWrite s ()
Writes a file in the HVAC Monad. Transactional: this will only occur at the end of a composed HVAC action, on completion of a database commit. If we assume writing a file to be atomic, which it is not, HVAC actions affecting files will have roughly ACID properties: all files will be locked and then written on successful conclusion of an HVAC action. Exceptions due to file permission errors will be checked for when the hvWriteFile action is first executed. If the HVAC action does not conclude successfully, or is rolled back, no files will be modified. However, if an exception is thrown partway through processing a set of file modifications, prior modifications *will not* be rolled back.
hvAppendFile :: FilePath -> String -> HCGI ReadWrite s ()
Appends to a file in the HVAC Monad. Properties are as with hvWriteFile.
hvWriteUniqueFile :: FilePath -> String -> HCGI ReadWrite s FilePath
Writes to a new, unique file in the HVAC Monad. Properties are as with hvWriteFile. If the filename it is passed already exists, it adds digits before the suffix until it encounters one that does not. The name of the file written to is returned.
Session Actions
getSes :: (MonadState (HVPar a) m, MonadSTM m) => m (Maybe a)
Reads the session. getSes :: HCGI q a (Maybe a)
putSes :: Maybe s -> HCGI ReadWrite s ()
Writes the session, tagging the HCGI action as one which modifies state.
modifySes :: (Maybe s -> (Maybe s, b)) -> HCGI ReadWrite s b
Modifies the session, tagging the HCGI action as one which modifies state.
Database Actions
select :: (MonadState (HVPar p) m, MonadSTM m, Functor m, MapFromTuple a SqlValue) => [Char] -> a -> m [Map String SqlValue]
Performs a select query on the database, based on the HDBC API. e.g., select "* from foo where id = ?" [toSql (12::Int)] Strict, returns a list of rows, indexed by key.
selectRow :: (MonadState (HVPar p) m, MonadSTM m, Functor m, MapFromTuple a SqlValue) => [Char] -> a -> m (Map String SqlValue)
As with select but only returns the first matching row. Strict.
selectVal :: (MonadState (HVPar p) m, MonadSTM m, Functor m, MapFromTuple a SqlValue) => [Char] -> a -> m SqlValue
As with select but only returns the first value of the first row. Strict.
execStat :: MapFromTuple a SqlValue => String -> a -> HCGI ReadWrite s Integer
Executes a statement in the database, based on the HDBC API, tagging the HCGI action as one which modifies state. e.g., execute "delete from foo where id = ?" [toSql (12::Int)] Strict, returns an integer indicating the number of modified rows.
STM Actions
hvReadTVar :: MonadSTM m => TVar a -> m a
Reads a TVar in an STM Monad.
hvWriteTVar :: TVar a -> a -> HCGI ReadWrite s ()
Writes a TVar in the HCGI Monad, tagging the HCGI action as one which modifies state.
hvModifyTVar :: TVar a -> (a -> (a, b)) -> HCGI ReadWrite s b
Modifies a TVar in the HCGI Monad, tagging the HCGI action as one which modifies state.
Lifted STM consistency primitives
gAlwaysSucceeds :: MonadSTM m => STM a -> m ()
This and the following are STM combinators lifted to generic STM-based monads. See the documentation for alwaysSucceeds and friends for more information.
gAlways :: MonadSTM m => STM Bool -> m ()
gRetry :: MonadSTM m => m a
gCheck :: MonadSTM m => Bool -> m a
Errors and Messages
addErrors :: [(String, String)] -> HCGI q s ()
Adds a list of (form) errors as field, error pairs.
addMsg :: String -> HCGI q s ()
Adds a single status message.
handleValidation
::
=> [(String, ValidationFunc s String a)]Input/Validator pairs
-> [(String, String)] -> HCGI q s CGIResultError handler
-> [a] -> HCGI q s CGIResultSuccess handler
-> HCGI q s CGIResult

Retrieves the specified inputs (named parameters set via GET or POST) and validates them with the paired function. If errors exist, the first handler is called, with the field/error pairs as a parameter. If no such errors exist, the inputs (as processed by the validators) are passed to the second handler. Validators are chained via Kleisi composition. i.e. trim >=> lengthAtLeast 2. Inputs that do not exist are treated as empty strings, for simplicity's sake.

Note that all validators must be of the same type, and the list passed to the success handler is therefore of a uniform type.

withValidation
::
=> [(String, ValidationFunc s String a)]Input/Validator pairs
-> [a] -> HCGI q s CGIResultSuccess handler
-> HCGI q s CGIResult
As with handleValidation, but rather than taking a specific error handler, simply adds errors to state via the addErrors function and then, via a call to continue, processing "falls through" to the next HCGI action.
Errors and Messages (Experimental)
handleValidation' :: (MapFromTuple a (String, ValidationFunc s String Dynamic), MapToTuple a1 Dynamic, TupleMatchTwo a a1) => a -> ([(String, String)] -> HCGI r s t) -> (a1 -> HCGI r s t) -> HCGI r s t
handleValidation specialized to heterogeneous tuples. Rather than a list, takes an arbitrary tuple, and rather than a list, returns a tuple. Tuples may be of up to ten items. For a tuple of one item, use Box. Still mildly experimental.
withValidation' :: (MapFromTuple a (String, ValidationFunc s String Dynamic), MapToTuple a1 Dynamic, TupleMatchTwo a a1) => a -> (a1 -> HCGI r s t) -> HCGI r s t
see 'handleValidation\''
newtype Box a
A box, i.e. a one-tuple. Used for functions that take and return tuples when you want to us a tuple with only one element.
Constructors
Box a
show/hide Instances
Has Author Post (Box Int)
StaticRecord Author (Box Int)
Sat (MapFromTupleD a x) => MapFromTuple (Box a) x
Sat (MapToTupleD a x) => MapToTuple (Box a) x
OneVal (Box a) a
TupleMatch (Box (mk a)) (Box a)
TupleMatchTwo (Box ((,) foo (bar -> mk a))) (Box a)
Running HVAC
data HVConf a
A set of config parameters
Constructors
HVConf
threads :: IntNumber of process threads to run
dbConn :: IO HVDBDB connection function.
sessTimeout :: Intin seconds
sessPoll :: IntInterval for cleaning sessions, in seconds, maximum of 2147.
sessPersist :: a -> IO ()Function for persisting session data on timeout.
cacheTime :: Intin seconds.
hvConf :: HVConf a
The default parameters. () is a dummy instance of HDBC.IConnection, exported by the HVAC library. { threads = 10, db = HVDB $ return () , sessTimeout = 3600, sessPoll = 300, sessPersist = const (return ()), cacheTime = 60 }.
runHVAC :: HVConf s -> HCGI q s CGIResult -> IO ()
Given a configuration and an HCGI action, built, one would imagine, with the provided combinators, runs a FastCGI server. Somewhat analogous to runFastCGIConcurrent. A minimal HVAC application takes the form of main = runHVAC hvConf (output "hello world")
CGI Methods
continue :: Alternative f => f a
Continue ends the current HCGI action, leaving all modifications to state intact. If there is a further alternative in the chain, execution will continue with it, otherwise a 404 will be returned. If a 404 is indeed returned, all modifications will be rolled back.
requestURL :: MonadCGI m => m String
The full path (relative) to the current page
refererURL :: MonadCGI m => m String
Referer URL. Empty if nonextant.
pathInfo :: MonadCGI m => m String
Overrides standard pathInfo to deal with lighttpd bug where all path information is accidentally lowercased.
logHVAC :: MonadSTM m => String -> m ()
Logs some message using the server's logging facility. Currently, this just writes it to stderr.
outputException :: (MonadCGI m, MonadSTM m, Functor m) => Exception -> m (CGIResult)
Output a 500 Internal Server Error with information from an Exception.
outputError
:: (MonadCGI m, MonadSTM m, Functor m)
=> IntHTTP Status code
-> StringStatus message
-> [String]Error information
-> m (CGIResult)
Output an error page to the user, with the given HTTP status code in the response. Also logs the error information..
catchCGI :: HCGI q s a -> (Exception -> HCGI q s a) -> HCGI q s a
outputNotFound
:: (MonadSTM m, MonadCGI m, Functor m)
=> StringThe name of the requested resource.
-> m (CGIResult)
Use outputError to output and log a 404 Not Found error.
outputMethodNotAllowed
:: (MonadSTM m, MonadCGI m, Functor m)
=> [String]The allowed methods.
-> m (CGIResult)
Use outputError to output and log a 405 Method Not Allowed error.
outputInternalServerError
:: (MonadSTM m, MonadCGI m, Functor m)
=> [String]Error information.
-> m (CGIResult)
Use outputError to output and log a 500 Internal Server Error.
lifting from STM
stmToHCGI :: STM a -> HCGI ReadWrite s a
stmToReadOnlyHCGI :: STM a -> HCGI ReadOnly s a
Produced by Haddock version 2.0.0.0