README.md

# Ignorant

Because sometimes ignorance is bliss.

Ignorant defines a protocol for data structures that may selectively ignore specific values they
contain, usually to simplify partial comparison in tests.

## Motivation
Sometimes it's useful to compare data structures in tests while ignoring pieces of data that are not
deterministic - auto-generated primary keys, calculated timestamps, and so on. Ignoring certain bits
ensures the _shape_ of the data is correct when it's not possible to enforce its values.

## Example

An implementation for `Map` is provided with that package for the purpose described above. Suppose
you have an API endpoint that fetches a record from the database and returns JSON data which
translates to this `response` map:

```elixir
%{
  id: 37,
  name: "Jim"
}
```

Now let's say you want to write a test to ensure that it returns the right data. Problem is, the
`id` field is auto-generated and therefore has no deterministic value between test runs. You could
use pattern matching...

```elixir%{a}
assert %{id: _, name: "Jim"} = response
```

...but you can't assign the value on the left side to a variable (or module attribute) and reuse it,
and if the match fails you don't get a nice diff message. That's where `Ignorant` comes in:

```elixir
assert %{id: :ignored, name: "Jim"} == Ignorant.ignore(response, [:id])
```

The left side is now a proper value that can be put in a variable or attribute and reused. Also note
the `==` (equality) assertion instead of `=` (match) assertion. That gives us a pretty diff that
highlights exactly what doesn't look the same on both sides, and is also stricter (which is a good
idea in tests).

It quickly becomes annoying and error-prone to tag things as `:ignored` in one side of the
comparison and include the corresponding field on the second parameter in the call to `ignore/2` on
the other side, so we can clean that up by using `merge_ignored/2`:

```elixir
expected = %{id: :ignored, name: "Jim"}
assert expected == Ignorant.merge_ignored(response, expected)
```

`merge_ignored` will walk through the `expected` map extracting all fields tagged as `:ignored`, and
then apply those to `response` while keeping all other values intact. The resulting map should be
equal to `expected`. If not, you'll get a pretty diff explaining what's different.


## Installation

1. Add `ignorant` to your list of dependencies in `mix.exs`:

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

2. Ensure `ignorant` is started before your application:

  ```elixir
  def application do
    [applications: [:ignorant]]
  end
  ```