EvReact is a very lightweight library introducing an event-based orchestrator for reactive programming. In essence you can describe workflows orchestrated by events that advance the workflow and trigger the events associated with its nodes. Suave.EvReact is a very simple adapter for Suave that transforms HTTP requests into events to be orchestrated by EvReact. It is useful whenever you need a specific sequence in URL invocations and ensure that a URL is inaccessible in a given state.
Here is a very simple example
open Suave.EvReact
open Suave
open Suave.Http
open Suave.Successful
open Suave.Web
open Suave.EvReact
open EvReact
open EvReact.Expr
// Create the EvReact events associated with URLs
let start = HttpEvent()
let work = HttpEvent()
let stop = HttpEvent()
let status = HttpEvent()
let jobs = ResizeArray<string>()
// chooseEvents is the only combiner currently featured by Suave.EvReact
// The list is (regex, event, default)
// Whenever the regex is matched by Suave the event is fired.
// The default web part can be overridden by assigining the Result property
// in the event
// In this example we have jobs that are started by accessing /start/id
// You perform some work only if the job is running with /work/id/arg
// You stop the job using /stop/id
let app = chooseEvents
[
("/start/(\\d+)", start, NO_CONTENT)
("/work/(\\d+)/(\\d+)", work, NO_CONTENT)
("/stop/(\\d+)", stop, NO_CONTENT)
("/status", status, NO_CONTENT)
]
// This EvReact net simply react to the status event by printing the list of jobs
let statusReq = !!status.Publish |-> (fun arg -> arg.Result <- OK (System.String.Join("<br/>", jobs)))
// Useful net generator expressing a loop until
let loopUntil terminator body = +( body / terminator ) - never
// The orchestrator used to run the nets
let orch = EvReact.Orchestrator.create()
// When start is received the function gets triggered
let startNet = !!start.Publish |-> (fun arg ->
// Read the id from the argument
let id = arg.Match.Groups.[1].Value
jobs.Add(id)
// Set the response
arg.Result <- OK (sprintf "Started job %s" id)
// The net performing the actual work is triggered only if the id is the one started
let doWork = (work.Publish %- (fun arg -> arg.Match.Groups.[1].Value = id)) |-> (fun arg ->
let value = int(arg.Match.Groups.[2].Value)
arg.Result <- OK ((value + 1).ToString())
)
// We get the stop event and only if relates to the current id trigger the stopNet event
let stopNet = HttpEvent()
let stopThis = (stop.Publish %- (fun arg -> arg.Match.Groups.[1].Value = id))
|-> (fun arg -> arg.Result <- OK(sprintf "Job %s done" id)
jobs.Remove(id) |> ignore
stopNet.Trigger(arg)
)
// Start a net listening for the stop event
Expr.start HttpEventArgs.Empty orch stopThis |> ignore
// Net looping forever unless the stopNet event fires
let net = (loopUntil [|stopNet.Publish|] doWork)
// Starts the net
Expr.start HttpEventArgs.Empty orch net |> ignore
)
// Starts the startNet and statusReq nets looping forever
Expr.start HttpEventArgs.Empty orch (+startNet)
Expr.start HttpEventArgs.Empty orch (+statusReq)
// Starts Suave
startWebServer defaultConfig app