π•°π–π–›π–†π–‘π–Žπ–‡π–šπ–— (Exvalibur) β†’ Smart Validation In Elixir

Exvalibur is the generator for blazingly fast validators of maps based on sets of predefined rules.

Under the hood, it generates a module with many different clauses of the same valid?/1 function with explicitly hardcoded pattern matches and guards on the input.

Here is the blog post shedding a light on what goodness it provides and h)ow is it implemented β†’ https://dev.to/mudasobwa/--smart-validation-in-elixir-4bcm

16 Likes

As I continue working on Exvalibur, I have implemented custom guards and pattern matching of values.

I just blogged about how I used custom sigils to allow quoted expressions in the rules.

4 Likes

Heh, interesting idea there!

Where exactly? Do you mean a validator module generation? If yes, I can assure you it is worth it: we use it in production for several months and it shows itself :+1:

Depending on input, it runs 10–1000 times faster than standard validations.

1 Like

Starting with v0.8.0 Exvalibur accepts module-based validatiors:

defmodule Validator do
  use Exvalibur, rules: [
    %{
      matches: %{currency_pair: <<"EUR", _ :: binary>>},
      conditions: %{foo: %{min: 0, max: 100}},
      guards: %{num: num > 0 and num < 100}}]
end

Validator.valid?(%{currency_pair: "EURUSD", foo: 50, num: 50})
#β‡’ {:ok, %{currency_pair: "EURUSD", foo: 50, num: 50}}
Validator.valid?(%{currency_pair: "USDEUR", foo: 50, num: 50})
#β‡’ :error
3 Likes

I tend to think that there is a convention that functions with ? should return boolean type

3 Likes

True that, but I struggled to find a better name to be short and self-explanatory. OTOH, I do not want to miss the opportunity to return what was indeed validated on success.

Maybe I should go with validate/1 to return what valid?/1 currently returns and valid?/1 to return boolean.

6 Likes

For what it’s worth, here’s the convention about trailing question marks and boolean return values: https://hexdocs.pm/elixir/master/naming-conventions.html#trailing-question-mark-foo

4 Likes

This is of course subjective, but personally I think that proposal is indeed more self-evident. :slight_smile:

1 Like

It surely is. The only thing that prevents me of doing it straight away is it’s breaking change. I probably will go with deprecation warnings and validate/1 doing exactly what valid?/1 does now and change the behaviour of valid?/1 to return boolean as of v1.0.0.

2 Likes
    @deprecated "Use #{__MODULE__}.validate/1 instead"
    def valid?(any), do: validate(any)

β†’ v0.9.0.

2 Likes

Custom validations in the modules using Exvalibur. @behaviour Exvalibur.Validatable.

defmodule CustomValidator do
  use Exvalibur, rules: [
    %{conditions: %{foo: %{min: 0, max: 10}}}]

  @impl Exvalibur.Validatable
  def custom_validate(%{foo: foo}),
    do: if foo == 42, do: {:ok, %{foo: 42}}, else: :error
end

And then:

CustomValidator.validate(%{foo: 5})
{:ok, %{foo: 5}}
CustomValidator.validate(%{foo: 50})
:error
CustomValidator.validate(%{foo: 42})
{:ok, %{foo: 42}}

β†’ v0.10.0.

1 Like

Can you give an example on how would you use Exvalibur to validate lists without using custom validators? Say, I allow only one instance of several different structs in it, and then I allow anywhere from 1 to 5000 instances of another struct, and another struct can only appear zero or one times, etc.

I am aware I can write my own custom validators for it. I am asking if you plan on adding validation of lists of maps/structs.

Is it implied by XSD? I am asking because next major update would be to support XSD as a validator definition.

If not, could you please fill in the issue? I am positive that is a great feature even outside of XSD parsing, so I’d implement it in any case. We might discuss the syntax for this, but I’d strongly appreciate doing this in github for the sake of reference/history.

Done: Introduce validation of lists.