[ANN] dream-html & pure-html 3.5.2

Pleased to announce the release of dream-html 3.5.2, which actually spawns a new package pure-html: pure-html 3.5.2 (latest) · OCaml Package

This package offers the same functionality as dream-html, except without a Dream dependency, so you can use whatever web server you like, or even use it for other applications than web servers. It works exactly the same way as dream-html, except the top-level module is Pure_html:

open Pure_html
open HTML

let content = article [] [
  p [] [txt "Header"];
  p [] [txt "Body"];
]

pure-html has a runtime dependency only on the uri package.

11 Likes

Thanks again @yawaramin for doing this! Much appreciated.

1 Like

[ANN] dream-html & pure-html 3.6.0

Hello, I am happy to announce the following changes:

  • Added some htmx attributes that had been omitted. Now as far as I can tell we have complete coverage of all core attributes, additional attributes, and those used by core extensions.
  • Add a ?header:bool optional parameter to to_xml and pp_xml functions to conveniently render the XML header as part of the output.
3 Likes

[ANN] dream-html & pure-html 3.6.1, 3.6.2

A double announcement:

3.6.1: when in XML rendering mode, correctly render empty-value attributes as having an empty string value. Thanks to @jonsterling !

3.6.2: automatically switch to XML rendering mode when rendering SVG and MathML tags inside HTML rendering mode.

1 Like

[ANN] dream-html 3.7.0

Happy to announce the addition of a helper module for typed form decoding functionality. See the docs here: Form (dream-html.Dream_html.Form)

An example:

type user = { name : string; age : int option }

open Dream_html.Form

let user_form =
  let+ name = required string "name"
  and+ age = optional int "age" in
  { name; age }

let dream_form = ["age", "42"; "name", "Bob"]
let user_result = validate user_form dream_form
(* => Ok { name = "Bob"; age = Some 42 } *)

let error_result = validate user_form ["age", "none"]
(* => Error [("age", "error.expected.int"); ("name", "error.required")] *)

Astute readers may observe that this provides some convenience functionality beyond what Dream itself offers; to validate the above form and get a complete set of field validation errors using only Dream you would do something like:

let user_result = match dream_form with
  | ["age", age; "name", name] ->
    (match int_of_string age with
    | age -> Ok { name; age = Some age }
    | exception Failure _ -> Error ["age", "error.expected.int"])
  | ["name", name] -> Ok { name; age = None }
  | ["age", age] ->
    (match int_of_string age with
    | age -> Error ["name", "error.required"]
    | exception Failure _ -> Error ["age", "error.expected.int"; "name", "error.required"])
  | _ -> Error ["name", "error.required"]

And this is a form with only two fields. You can imagine how convoluted the logic would be for more complex forms. Of course, you might just decide to use List.assoc_opt and build up the validation errors, but even that can get tricky. So if you are making heavy use of HTML forms, a helper module that takes care of all these validation details can be very useful. Enjoy!

4 Likes

[ANN] dream-html 3.8.0

Happy to announce some added power to the form decoding functionality. Three main things:

  1. Added Dream_html.form and query helper functions to wrap extracting the data directly from the Dream request and decoding it correspondingly from the body or query.
  2. Added the (monadic) chaining operator Dream_html.Form.( let* ) and ok and error helpers to allow sophisticated sequential decoding where decoding of some fields depend on others.
  3. Added optional parameters to constrain typed decoding of values eg int ~min:0 will succeed the decode if the value is an integer and at least 0. Also added unix_tm type decoder to decode timestamps into Unix.tm structs (not timezone-aware).

The last example on the page shows a fairly sophisticated form decoder which requires an id field and one or more of the fields days, weeks, months, and years, and fails if at least one is not provided.

Enjoy :slight_smile:

2 Likes