README.md

# Spex [![Build Status](https://travis-ci.org/codegram/spex.svg?branch=master)](https://travis-ci.org/codegram/spex)

Spex helps you validate your values against value-based schemas.

## Installation

First, add Spex to your `mix.exs`

```elixir
def deps do
  [{:spex, "~> 0.1.0"}]
end
```

Then, update your dependencies:

    $ mix deps.get

## Usage

```elixir
import Spex
```

A spec is always a value. Let's start with the simplest spec, a primitive value:

### Primitive value specs

```elixir
my_simple_spec = 3

Spex.conforms?(my_simple_spec, 3)
# => true

Spex.conforms?(my_simple_spec, 5)
# => false

Spex.conforms?("foo", "foo")
# => true

Spex.conforms?(:bar, :bar)
# => true
```

Primitive values, such as integers, strings and atoms, validate values using
simple equality.

### List specs

Let's move on to a slightly more complex spec: a list. A list spec *must* have
one spec in it, which will validate against all the elements of the validated
list.

```elixir
my_simple_spec = 3

Spex.conforms?([my_simple_spec], [3, 3, 3, 3])
# => true

Spex.conforms?([my_simple_spec], [3, 3, 1, 9])
# => false

Spex.conforms?([my_simple_spec], [])
# => true
```

This last example might have surprised you -- but an empty list *always*
conforms to *any* list spec. If you want your spec to only validate non-empty
lists, we've got you covered:

```elixir
use Spex.DSL

my_simple_spec = 3

Spex.conforms?(non_empty_list(my_simple_spec), [3, 3, 3])
# => true

Spex.conforms?(non_empty_list(my_simple_spec), [])
# => false
```

### Predicate specs

Predicate functions (functions that return true or false) are naturally a very
useful kind of spec too:

```elixir
Spex.conforms?(fn(x) -> x > 3 end, 8)
# => true

Spex.conforms?(fn(x) -> x > 3 end, 1)
# => false

import Integer

Spex.conforms?(&Integer.is_odd/1, 3)
# => true
```

### Map specs

Specs really shine when they can describe arbitrarily nested maps. Map specs to
the rescue:

```elixir
Spex.conforms?(%{person: %{age: fn(x) -> x > 21, name: &Kernel.is_string/1}},
               %{person: %{age: 83, name: "James"}})
# => true

Spex.conforms?(%{person: %{age: fn(x) -> x > 21, name: &Kernel.is_string/1}},
               %{person: %{age: 10, name: 9}})
# => false
```

### Tuple specs

Tuple specs work pretty much like you'd expect:

```elixir
Spex.conforms?({3, fn(x) -> x > 3 end}, {3, 4})
# => true

Spex.conforms?({3, fn(x) -> x > 3 end}, {3, 1})
# => false

Spex.conforms?({3, fn(x) -> x > 3 end}, {1, 4})
# => false
```

### Non-nil specs

Sometimes we don't care what a value is, as long as it is non-nil. `any` is just
for that:

```elixir
use Spex.DSL

Spex.conforms?(any, 3)
# => true

Spex.conforms?(any, nil)
# => false
```

### Optional specs

Sometimes we want to validate a value allowing it to be nil. `optional` is what
we want:

```elixir
use Spex.DSL

Spex.conforms?(optional(3), 3)
# => true

Spex.conforms?(optional(3), nil)
# => true

Spex.conforms?(optional(3), 4)
# => false
```

Naturally, map specs can describe optional keys:

```elixir
use Spex.DSL

Spex.conforms?(%{optional(:name) => "foo"}, %{})
# => true

Spex.conforms?(%{optional(:name) => "foo"}, %{name: "bar"})
# => false
```

And optional values:

```elixir
Spex.conforms?(%{name: optional("foo")}, %{name: nil})
# => true

Spex.conforms?(%{name: optional("foo")}, %{name: "bar"})
# => false
```

### `validate/2` and `validate!/2`

To make specs easier to use within the rest of your code, you can validate a
value against a spec with `validate/2`:

```elixir
Spex.validate(3, 3)
# => {:ok, 3}

Spex.validate(3, 4)
# => {:error, "Value doesn't conform to spec: \n\nValue: 4\n\nSpec: 3"}
```

Or if you'd like to either get the validated value or raise an error,
`validate!/2` is your friend:

```elixir
Spex.validate!(3, 3)
# => 3

Spex.validate!(3, 4)
# raises Spex.ValidationError, "Value doesn't conform to spec: \n\nValue: 4\n\nSpec: 3"
```

## Copyright

Copyright (c) 2016 Codegram. See LICENSE for details.