README.md

# Gate
[![Hex.pm](https://img.shields.io/hexpm/v/gate.svg)](https://hex.pm/packages/gate)
[![CircleCI](https://circleci.com/gh/gmartsenkov/gate.svg?style=svg)](https://circleci.com/gh/gmartsenkov/gate)

This is a simple API for validating data structures, mostly from user input like web forms or API requests.

## Table of Contents
- [Installation](#installation)
- [Usage](#usage)
- [Rules](#rules)
- [Custom Rules](#custom-rules)
- [Error Messages](#error-messages)
- [Licence](#licence)

## Installation

the package can be installed
by adding `gate` to your list of dependencies in `mix.exs` and run `mix deps.get`:

```elixir
def deps do
  [
    {:gate, "~> 0.1.9"}
  ]
end
```

## Usage
Gate's error message handling requires a JSON parser, a decoder must be set in `config/config.exs`

``` elixir
# config/config.exs

use Mix.Config
config :gate, decoder: Poison
```

Example:
```elixir
  @schema %{
    "name" => :str
    "age" => :int,
    "gender" => {:include, ["male", "female"]},
    "pet_names" => {:each, :str},
    "more_info" => %{
      "telefone" => [:str, {:regex, ~r/custom_regex/}],
      "address" => [:optional, :str]
    },
    "relationships" => {:each,
      %{ "id" => :int, "type" => {:equal, "user" } }
    }
  }

  form_data = %{
    "name" => "Jon",
    "age" => 21,
    "gender" => "male",
    "pet_names" => ["jekyll", "hyde"],
    "exra_field" => "something", # It'll be ignored
    "more_info" => %{
      "telefone" => "custom_regex",
    },
    "relationships" => [
      %{ "id" => 1, "type" => "user" },
      %{ "id" => 2, "type" => "user" }
    ]
  }
  
  Gate.valid?(form_data, @schema)
  # { 
  #   :ok, 
  #   %{
  #     "name" => "Jon",
  #     "age" => 21,
  #     "gender" => "male",
  #     "pet_names" => ["jekyll", "hyde"],
  #     "more_info" => %{
  #       "telefone" => "custom_regex",
  #     },
  #    "relationships" => [
  #      %{ "id" => 1, "type" => "user" },
  #      %{ "id" => 2, "type" => "user" }
  #    ]
  #   } 
  # }
```

## Rules

Type checks available:
* `:int`
* `:str`
* `:float`
* `:list`
* `:atom`
* `:bool`
* `:map`
* `:tuple`
* `:number`

**You can make an attribute optional with `:optional`**

More advanced rules are:
* `{ :equal, 5 }` will check if the value is equal to 5
* `{ :not_equal, 5 }` will check if the value is not equal to 5
* `{ :include, ["option1", "option2"]}` will check if the value is in the List
* `{ :regex, ~r/custom_regex/ }` will try and match the value against the Regex

**You can validate a field with multiple rules by using a list - `[:str, {:equal, "spaghetti"}, {:custom, custom_rule()}]`**

If you wanna check the value of each element in a list you can use `{:each, rule}` for example:
* `{:each, :int}` - will check if all elements are integers
* `{:each, {:include, ["a", "list", "of", "options"]}}` - will check that each value is in the list
* `{:each, %{ "id" => :int} }` - when you pass a map it'll check all elements against that map
## Custom Rules

Example custom rule without the use of locales:
```elixir
  def custom_rule do
    fn(value) ->
      if value == 1, do: true, else: "Value not equal to 1"
    end
  end
  
  Gate.valid?(1, {:custom, custom_rule()})
  # true
  Gate.valid?(2, {:custom, custom_rule()})
  # "Value not equal to 1"
```

If you want to make use of custom ([Locales](#error-messages)) you can do something like:
```elixir
  def custom_rule do
    fn(value) ->
      if value == 1, do: true, else: { :locale, "custom_rule1" }
      # If you want to use the value in the locale 
      # you can pass it as a third argument like
      # if value == 1, do: true, else: { :locale, "custom_rule1", value }
    end
  end
```

## Error messages
The default error messages are defined in [here](https://github.com/gmartsenkov/gate/blob/master/assets/default_locale.json).
They can be overridden by specifying your custom locale file in your `config/config.exs`

``` elixir
# config/config.exs

use Mix.Config
config :gate, locale_file: "assets/locale.json"
```
Example `locale.json`
``` json
{
  "int": "This will override the default int type check error",
  "custom_rule1": "Value does not match custom_rule1",
}
```
Example custom locale that changes the default `:int` rule and also expose the value:
``` json
{
  "int": "{} is not an integer"
}
```
`{}` will be replaced with the value that is being validated
``` elixir
  Gate.valid?("spaghetti", :int)
  # "spaghetti is not an integer"
```

## License

[MIT](LICENSE) © gmartsenkov