 | hvac-0.1: Http view and controller. A transactional, declarative framework for lightweight web applications. | Contents | Index |
|
| Network.Frameworks.HVAC | | Portability | portable | | Stability | experimental | | Maintainer | s.clover@gmail.com |
|
|
|
|
|
| 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 {} | | | 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.
| Instances | |
|
|
| 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 | |
| Instances | |
|
|
| 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 CGIResult | Error handler
| | -> [a] -> HCGI q s CGIResult | Success 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 CGIResult | Success 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 | | Instances | |
|
|
| Running HVAC
|
|
| data HVConf a |
| A set of config parameters
| | Constructors | | HVConf | | | threads :: Int | Number of process threads to run
| | dbConn :: IO HVDB | DB connection function.
| | sessTimeout :: Int | in seconds
| | sessPoll :: Int | Interval for cleaning sessions, in seconds, maximum of 2147.
| | sessPersist :: a -> IO () | Function for persisting session data on timeout.
| | cacheTime :: Int | in 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) | | | => Int | HTTP Status code
| | -> String | Status 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) | | | => String | The 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 |