3
64%
MIT
BucklesScript bindings to the validationjs library

Build Status Coverage Status

bs-validation

A BuckleScript implementation of the Folktale validation applicative

NOTE: These are not bindings, this a ReasonML implementation of the Validation applicative.

Why?

I wanted a way to do validations for my server side project.

Status

Not all of the Folktale/Validation functions will be implemented. Here is a list of the currently implemented functions:

  • map
  • apply
  • unsafeGet
  • getOrElse
  • orElse
  • concat
  • fold
  • swap
  • bimap
  • mapFailure
  • toOption
  • merge see note in the code.

All implemented functions are found in src/Validation.re, They are all documented with their Folktale style doc strings.

How do I install?

  1. Add the bs-validation package to your project.

    yarn add bs-validation
  2. Add bs-validation to your bsconfig.json

    {
      "dependencies": [ "bs-validation" ]
    }
  3. Enjoy!

Usage

The library is exposed as a functor which accepts modules that implement the following type interface:

  • RE
  • ML
module type Foldable = {
  type t('a);
  let concat: (t('a), t('a)) => t('a);
};
module type Foldable  = sig type 'a t val concat : 'a t -> 'a t -> 'a t end

All of the examples use an array based implementation of the Foldable type:

  • RE
  • ML
module FoldableArray = {
  type t('a) = array('a);
  let concat = (x, y) => Belt_Array.concat(x, y);
};
module FoldableArray =
  struct type 'a t = 'a array
         let concat x y = Belt_Array.concat x y end

You import the module into your project by calling the Validation functor with your version of the Foldable type.

  • RE
  • ML
module V = Validation.Make_validation(FoldableArray);
module V = Validation.Make_validation(FoldableArray)

Then you can use it to validate all of your things!

  • RE
  • ML
let lengthError = "Password must have more than 6 characters.";
let strengthError = "Password must contain a special character.";

let isPasswordLongEnough = (password) =>
  String.length(password) > 6
    ? V.Success(password)
    : V.Failure([|lengthError|]);

let isPasswordStrongEnough = (password) => {
  let regex = [%bs.re "/[\\W]/"];
  Js.Re.test(password, regex)
    ? V.Success(password)
    : V.Failure([|strengthError|])
};

let isPasswordValid = (password) => {
  V.Success()
  |> V.concat(isPasswordLongEnough(password))
  |> V.concat(isPasswordStrongEnough(password))
  |> V.map((_) =>password)
};


describe("Folketale password validation example", () => {
  test("should return the password", () => {
    let password = "rosesarered$andstuff";
    switch (isPasswordValid(password)) {
    | Failure(f) => { Js.log(f); fail("unexpected_failure") }
    | Success(p) => Expect.expect(p) |> Expect.toBe(password)
    }
  });

  test("should return a single item failure", () => {
    let password = "rosesarered";
    switch (isPasswordValid(password)) {
    | Failure(f) => Expect.expect(f) |> Expect.toBeSupersetOf([|strengthError|])
    | Success(_) => fail("unexpected_success")
    }
  });

  test("should return 2 items in the failure list", () => {
    let password = "foo";
    switch (isPasswordValid(password)) {
    | Failure(f) => {
        Expect.expect(f)
        |> Expect.toBeSupersetOf([|lengthError, strengthError|])
      }
    | Success(_) => fail("unexpected_success")
    }
  });
});
let lengthError = "Password must have more than 6 characters."
let strengthError = "Password must contain a special character."
let isPasswordLongEnough password =
  match (String.length password) > 6 with
  | true  -> ((V.Success (password))[@explicit_arity ])
  | false  -> ((V.Failure ([|lengthError|]))[@explicit_arity ])
let isPasswordStrongEnough password =
  let regex = [%bs.re "/[\\W]/"] in
  match Js.Re.test password regex with
  | true  -> ((V.Success (password))[@explicit_arity ])
  | false  -> ((V.Failure ([|strengthError|]))[@explicit_arity ])
let isPasswordValid password =
  ((((V.Success (()))[@explicit_arity ]) |>
      (V.concat (isPasswordLongEnough password)))
     |> (V.concat (isPasswordStrongEnough password)))
    |> (V.map (fun _  -> password))
let _ =
  describe "Folketale password validation example"
    (fun ()  ->
       test "should return the password"
         (fun ()  ->
            let password = "rosesarered$andstuff" in
            match isPasswordValid password with
            | ((Failure (f))[@explicit_arity ]) ->
                (Js.log f; fail "unexpected_failure")
            | ((Success (p))[@explicit_arity ]) ->
                (Expect.expect p) |> (Expect.toBe password));
       test "should return a single item failure"
         (fun ()  ->
            let password = "rosesarered" in
            match isPasswordValid password with
            | ((Failure (f))[@explicit_arity ]) ->
                (Expect.expect f) |>
                  (Expect.toBeSupersetOf [|strengthError|])
            | Success _ -> fail "unexpected_success");
       test "should return 2 items in the failure list"
         (fun ()  ->
            let password = "foo" in
            match isPasswordValid password with
            | ((Failure (f))[@explicit_arity ]) ->
                (Expect.expect f) |>
                  (Expect.toBeSupersetOf [|lengthError;strengthError|])
            | Success _ -> fail "unexpected_success"))