Suave.evreact

Suave.EvReact

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