-
-
Notifications
You must be signed in to change notification settings - Fork 140
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Decode form into custom type #66
Comments
For this particular use case, would this work? match%lwt Dream.form request with
| `Ok form ->
let text = List.assoc "text" form in
(* ... *)
| _ -> Dream.empty `Bad_Request (Note: I am willing to consider more generic decoders, of course, just want to ask first!) |
(Note that more robust code would probably use |
Yes, that will definitely work. I suppose that reduces this request to more about type-safety ergonomics :-) If I may provide another data point, the Play Framework does something very similar to what I suggested: https://www.playframework.com/documentation/2.8.x/ScalaForms It has what it calls a 'mapping' which describes how to decode the form into a custom type. It helps to decode and validate the form in one shot. They provide decoding helpers for emails, dates, times, decimal numbers. Very useful. |
From the top of my head there are conformist (used by Sihl) and decoders that implement this mapping functionality, including validation. I think one of the route we could consider is to make Dream.form returns compatible values with either of these libraries. Or, having helper functions on a separate library like dream-conformist or something like that. |
Thanks @yawaramin and @tungd. I think separate dream-conformist or dream-decoders is good to get started. I was originally considering creating some kind of deriving_form, too, which might eliminate boilerplate completely, as it would define the schema/decoder automatically, based on the OCaml type. |
I've looked at val to_conformist: (string * string) list -> (string * string list) list
(* Implementation *)
let to_conformist : (string * string) list -> (string * string list) list =
fun dream_inputs ->
List.map (fun (field, value) -> (field, [ value ])) dream_inputs Advance use cases would be look for this pattern:
And turn We can also provide some built-in validators (could be
See oxidizing/conformist#7 for more details Update: maybe we don't need |
I have something working that looks like this: type user =
{ name : string;
id : int;
age : int option
}
let user_form =
let+ name = ensure "Must not be empty" (( <> ) "") required "name" string
and+ id = ensure "Must be > 0" (( < ) 0) required "id" int
and+ age =
ensure "Must be > 16"
(Option.fold ~none:true ~some:(( < ) 16))
optional "age" int
in
{ name; id; age }
let user = validate "user-form" user_form [] With the result: Error
(`Assoc
[("user-form",
`Assoc
[("id", `String "Please fill out this field");
("name", `String "Please fill out this field")])]) And happy path: # let user = validate "form-login" form ["name", "Bob"; "id", "16"];;
val user :
(user,
[> `Assoc of
(string * [> `Assoc of (string * [> `String of string ]) list ]) list ])
result = Ok {name = "Bob"; id = 16; age = None} Would there be interest in something like this? I can also consider putting it in dream-html as form validation could be loosely said to be related to HTML... |
I would start with putting it into a downstream library + examples and links from the Dream docs. |
OK, added to dream-html and will publish it soon. Some examples https://yawaramin.github.io/dream-html/dream-html/Dream_html/Form/index.html#examples |
Update ocamlformat to 0.23.0
This is a proposal to add a function
Dream.form_decode
(e.g., name is not super important) to decode a form's fields into a custom type, say e.g.type person = { name : string; id : int }
. Fortunately,'a form_result
is generic already which leads me to think you possibly had something like this in mind already.Justification: currently
Dream.form
returns an association list of form keys and values. This forces us to pattern match on every form field in order. Sometimes we don't want to handle all form fields, and instead just a few of them. E.g., when writing a Slack slash-command, you set up a POST endpoint that receives a form body with several fields: https://api.slack.com/interactivity/slash-commands#app_command_handlingSo we would need to pattern match on
token
,team_id
, etc. to get to the one field we often really want,text
. Instead of that, if we could define a form decoder, we could selectively pick out only the fields we want. E.g.:The text was updated successfully, but these errors were encountered: