From 98e5fc183b87552017d5d928ba2416fbaa814945 Mon Sep 17 00:00:00 2001 From: Kevin COMBRIAT Date: Wed, 13 Jan 2021 13:31:02 +0900 Subject: [PATCH] refactor(structures): Use arrays instead of lists throughout the whole code --- example/App.res | 7 ++++--- example/Components.res | 3 +-- example/Validations.res | 37 ++++++++++++++++------------------ src/Formidable.res | 38 ++++++++++++++++++----------------- src/FormidableExtra.res | 12 +++-------- src/FormidableValidations.res | 28 +++++++++++++++++--------- 6 files changed, 64 insertions(+), 61 deletions(-) diff --git a/example/App.res b/example/App.res index ebba6df..546cde0 100644 --- a/example/App.res +++ b/example/App.res @@ -28,12 +28,13 @@ let makeForm = Formidable.make(~values=module(Values), ~error=module(I18n.Error) module Form = unpack(makeForm(~onSubmit=_values => (), ~onSubmitError=(_values, _errors) => (), ())) open Validations + // Validations can be defined in an other module, and re-used easily -let requiredValidations = list{(#onChange, required)} +let requiredValidations = [(#onChange, required)] -let emailValidations = list{(#onChange, required->compose(email))} +let emailValidations = [(#onChange, required->compose(email))] -let passwordConfirmValidations = list{(#onChange, equals(Values.password))} +let passwordConfirmValidations = [(#onChange, equals(Values.password))] module Child = { @react.component diff --git a/example/Components.res b/example/Components.res index 1c38bd7..c116e55 100644 --- a/example/Components.res +++ b/example/Components.res @@ -5,10 +5,9 @@ module Test = { module Errors = { @react.component - let make = (~value: list) => { + let make = (~value: array) => {
{value - ->List.toArray ->Array.map((#error(name, _) as error) =>
{("Field has errors: " ++ I18n.translate(error))->React.string}
) diff --git a/example/Validations.res b/example/Validations.res index 4075361..23d214e 100644 --- a/example/Validations.res +++ b/example/Validations.res @@ -1,35 +1,32 @@ include Formidable.Validations -open Validator.Args -open Optic -let required = ( - list{"required"}, - ({label, value}) => - if value == "" { - #error(#error(("required", label))) - } else { - #ok(value) +let required = { + Description.names: list{"required"}, + validator: ({label, value}) => + switch value { + | "" => #error(#error(("required", label))) + | _ => #ok(value) }, -) +} let emailRegEx = %re("/.+@.+/") -let email = ( - list{"email"}, - ({label, value}) => - if Js.Re.test_(emailRegEx, value) { +let email = { + Description.names: list{"email"}, + validator: ({label, value}) => + if emailRegEx->Js.Re.test_(value) { #ok(value) } else { #error(#error(("email", label))) }, -) +} -let equals = lens => ( - list{"equals"}, - ({label, value, values}) => - if Lens.get(lens, values) === value { +let equals = lens => { + Description.names: list{"equals"}, + validator: ({label, value, values}) => + if lens.Optic.Lens.get(values) == value { #ok(value) } else { #error(#error(("equals", label))) }, -) +} diff --git a/src/Formidable.res b/src/Formidable.res index 360570a..6d457dd 100644 --- a/src/Formidable.res +++ b/src/Formidable.res @@ -20,13 +20,13 @@ module type Handlers = { let onSubmit: option unit> - let onSubmitError: option<(values, list) => unit> + let onSubmitError: option<(values, array) => unit> } module States = { module Field = { module Status = { - type t<'error> = [#pristine | #valid | #touched | #errors(list<'error>)] + type t<'error> = [#pristine | #valid | #touched | #errors(array<'error>)] } @bs.deriving(accessors) @@ -77,8 +77,8 @@ module States = { let hasErrors = fields => fields->Map.String.some(_ => Field.hasErrors) let getErrors = fields => - fields->Map.String.reduce(list{}, (acc, _, field) => - field->Field.getErrors->Option.mapWithDefault(acc, errors => List.concat(acc, errors)) + fields->Map.String.reduce([], (acc, _, field) => + field->Field.getErrors->Option.mapWithDefault(acc, errors => acc->Js.Array2.concat(errors)) ) let reset = fields => fields->Map.String.map(Field.reset) @@ -185,7 +185,7 @@ module type Form = { ~onBlur: ReactEvent.Focus.t => unit=?, ~onChange: 'value => unit=?, ~onFocus: ReactEvent.Focus.t => unit=?, - ~validations: list>=?, + ~validations: array>=?, ~disable: bool=?, ~key: string=?, unit, @@ -198,7 +198,7 @@ module type Form = { "onBlur": option unit>, "onChange": option<'value => unit>, "onFocus": option unit>, - "validations": option>>, + "validations": option>>, "disable": option, } = "" @@ -211,7 +211,7 @@ module type Form = { "onBlur": option unit>, "onChange": option<'value => unit>, "onFocus": option unit>, - "validations": option>>, + "validations": option>>, "disable": option, }> } @@ -293,7 +293,7 @@ module Make = ( }) switch (Handlers.onSubmit, Handlers.onSubmitError, States.getErrors(fields)) { - | (Some(onSubmit), _, list{}) => onSubmit(values) + | (Some(onSubmit), _, []) => onSubmit(values) | (_, Some(onSubmitError), errors) => onSubmitError(values, errors) | _ => () } @@ -366,7 +366,7 @@ module Make = ( ~onBlur: ReactEvent.Focus.t => unit=?, ~onChange: 'value => unit=?, ~onFocus: ReactEvent.Focus.t => unit=?, - ~validations: list>=?, + ~validations: array>=?, ~disable: bool=?, ~key: string=?, unit, @@ -379,7 +379,7 @@ module Make = ( "onBlur": option unit>, "onChange": option<'value => unit>, "onFocus": option unit>, - "validations": option>>, + "validations": option>>, "disable": option, } = "" @@ -392,18 +392,20 @@ module Make = ( let (isFocused, setIsFocused) = React.useState(() => false) let validationNames = - props["validations"]->Option.mapWithDefault(list{}, validations => - validations->ListExtra.flatMap(Validations.getName) + props["validations"]->Option.mapWithDefault([], validations => + validations->ArrayExtra.flatMap(validation => + validation->Validations.getNames->List.toArray + ) ) - let hasValidation = name => validationNames->ListExtra.includes(name) + let hasValidation = name => validationNames->Js.Array2.includes(name) let validate = React.useCallback1((validationContext, values) => props["validations"]->Option.mapWithDefault(#valid, validations => - switch validations->List.keepMap(((strategy, (_, validation))) => + switch validations->Array.keepMap(((strategy, {validator})) => if Validations.shouldValidate(~context=validationContext, ~strategy) { - switch validation({ - Validations.Validator.Args.label: props["errorLabel"]->OptionExtra.alt( + switch validator({ + Validations.Validator.Args.label: props["errorLabel"]->OptionExtra.or( props["label"], ), lens: props["lens"], @@ -418,11 +420,11 @@ module Make = ( None } ) { - | list{} => #valid + | [] => #valid | errors => #errors(errors) } ) - , List.toArray(validationNames)) + , validationNames) let validateAndUpdate = (validationContext, values) => modifiers.updateField(props["name"], field => { diff --git a/src/FormidableExtra.res b/src/FormidableExtra.res index e9cd89a..6668123 100644 --- a/src/FormidableExtra.res +++ b/src/FormidableExtra.res @@ -1,15 +1,9 @@ -module ListExtra = { - let flatMap = (xs, f) => xs->List.reduce(list{}, (acc, x) => acc->List.concat(f(x))) - - let rec includes = (xs, y) => - switch xs { - | list{} => false - | list{x, ...xs} => x === y ? true : includes(xs, y) - } +module ArrayExtra = { + let flatMap = (xs, f) => xs->Array.reduce([], (acc, x) => acc->Js.Array2.concat(f(x))) } module OptionExtra = { - let alt = (option1, option2) => + let or = (option1, option2) => switch (option1, option2) { | (Some(_), _) => option1 | _ => option2 diff --git a/src/FormidableValidations.res b/src/FormidableValidations.res index cf8fa71..4d5d9a8 100644 --- a/src/FormidableValidations.res +++ b/src/FormidableValidations.res @@ -27,26 +27,36 @@ module Validator = { type t<'values, 'value, 'error> = Args.t<'values, 'value, 'error> => Value.t<'value, 'error> } -module Pair = { - type t<'values, 'value, 'error> = (list, Validator.t<'values, 'value, 'error>) +module Description = { + type t<'values, 'value, 'error> = { + names: list, + validator: Validator.t<'values, 'value, 'error>, + } } -type t<'values, 'value, 'error> = (Strategy.t, Pair.t<'values, 'value, 'error>) +type t<'values, 'value, 'error> = (Strategy.t, Description.t<'values, 'value, 'error>) + +module type Values = { + type t +} -let compose = ((name, validator), (name', validator')) => ( - List.concat(name, name'), - field => +let compose = ( + {Description.names: names, validator}, + {Description.names: names', validator: validator'}, +) => { + Description.names: names->List.concat(names'), + validator: field => switch validator(field) { | #ok(_) => validator'(field) | #error(_) as error => error }, -) +} let getStrategy = ((strategy, _)) => strategy -let getName = ((_, (name, _))) => name +let getNames = ((_, {Description.names: names})) => names -let getValidator = ((_, (_, validator))) => validator +let getValidator = ((_, {Description.validator: validator})) => validator /* * Returns true if a validation should be performed in the given context. If no context is provided, always returns true */