conformist

Conformist allows you to define schemas to decode, validate and sanitize input data declaratively
README

About

Conformist allows you to define schemas to decode, validate and sanitize input data declaratively. It comes with runtime types for primitive OCaml types such as int, string, bool, float and Ptime.t, option and custom types. Re-use business rules in validators and run it on the client side with js_of_ocaml. Arbitrary meta data can be stored in schemas which is useful to build functionality on top of conformist.

Typical use cases are enforcing invariants of models or user input sanitization.

In essence, conformist helps you to keep your runtime types/contracts in sync with your static types.

Installation

opam install conformist

In your dune file:

(executable
  (name app)
  (libraries
   ...
   conformist))

Usage

Let's look at an example.

type occupation =
  | Mathematician
  | Engineer

type user =
  { occupation : occupation
  ; email : string
  ; birthday : Ptime.t
  ; nr_of_siblings : int
  ; comment : string option
  ; favorite_shows : string list
  ; wants_premium : bool
  }

let user
    occupation
    email
    birthday
    nr_of_siblings
    comment
    favorite_shows
    wants_premium
  =
  { occupation
  ; email
  ; birthday
  ; nr_of_siblings
  ; comment
  ; favorite_shows
  ; wants_premium
  }
;;

let occupation_decoder = function
  | [ "mathematician" ] -> Ok Mathematician
  | [ "engineer" ] -> Ok Engineer
  | _ -> Error "Unknown occupation provided"
;;

let occupation_encoder = function
  | Mathematician -> [ "mathematician" ]
  | Engineer -> [ "engineer" ]
;;

(* This is for example purpose only. 
   Please use emile (https://github.com/dinosaure/emile) instead *)
let validate_email value =
  if String.index value '@' > 0 then None
  else Some "This doesn't look like an email"
  
let user_schema =
  Conformist.(
    make
      [ custom occupation_decoder occupation_encoder "occupation" ~meta:()
      ; string "email" ~validator: validate_email
      ; datetime "birthday"
      ; int ~default:0 "nr_of_siblings"
      ; optional (string "comment")
      ; list (string "favorite_shows")
      ; bool "wants_premium"
      ]
      user)
;;

let input =
  [ "occupation", [ "engineer" ]
  ; "email", [ "test@example.com" ]
  ; "birthday", [ "2020-12-01T00:00:00.00Z" ]
  ; "nr_of_siblings", [ "3" ]
  ; "comment", [ "hello" ]
  ; "favorite_shows", [ "Iron Man"; "Avengers" ]
  ; "wants_premium", [ "true" ]
  ]
;;

let user = Conformist.decode user_schema input
let validation_errors = Conformist.validate user_schema input

Try to delete/swap some lines of the list of fields, to change the constructor or the user type. The compiler forces you to keep these three things in sync.

Decoding doesn't validate the data, it just makes sure that the types are correct and translates strings to the correct static types.

Note that if decoding of a field fails, validation fails as well. Before a field is validated, it gets decoded.

Since we are shadowing the list [], dune warnings might fail compilation depending on the configuration. Suppress warning -40 can help.

Documentation

The documentation for the latest released version can be found here.

License

Copyright (c) 2020 Oxidizing Systems

Distributed under the MIT License. See LICENSE for more information.

Acknowledgements

The implementation of this project was inspired by archi and re-web.

Install
Published
05 Apr 2022
Sources
0.8.1.tar.gz
md5=28b7ee03002c0cf14c94209d86c17b9c
sha512=b06a1a72395ded0f8751bf6d123753e48a3853b4b3ab0263de39264eb3670a16c4f54d4f0be2f23a05223d63fc76452311bb444b3dbd1cf1a68eb713a4a998f6
Dependencies
odoc
with-doc
sexplib
>= "v0.13.0" & with-test
alcotest
>= "1.2.3" & with-test
ptime
>= "0.8.5"
ocaml
>= "4.08.0"
dune
>= "2.7"
Reverse Dependencies
sihl
>= "1.0.0~rc1"