# `Agenda.Predicate`
[🔗](https://github.com/elixir-tempo/agenda/blob/v0.3.0/lib/agenda/predicate.ex#L1)

The attribute predicate vocabulary — the words a requirement is
written in.

These deliberately mirror the duration predicates `Tempo` already
provides (`Tempo.at_least?/2`, `Tempo.at_most?/2`,
`Tempo.exactly?/2`). One vocabulary applies to durations on the
temporal side and to attributes here, so there is one set of words to
learn rather than two.

A predicate knows how to describe itself, which is what lets a failed
match explain *why* in a sentence rather than returning `false`.

### Examples

    iex> import Agenda.Predicate
    iex> satisfied?(at_least(8), 12)
    true

    iex> import Agenda.Predicate
    iex> describe(at_least(8))
    "at least 8"

# `op`

```elixir
@type op() :: :at_least | :at_most | :exactly | :any_of | :all_of | :none_of
```

The supported comparisons.

# `t`

```elixir
@type t() :: t(op())
```

A comparison a resource attribute must satisfy.

# `t`

```elixir
@type t(op) :: %Agenda.Predicate{op: op, operand: term()}
```

A comparison whose operator is known — `t(:at_least)` is what
`at_least/1` returns. The constructors are specified this precisely so
that a misplaced predicate is a type error rather than a runtime
surprise.

# `all_of`

```elixir
@spec all_of([term()]) :: t()
```

The attribute — itself a list — must contain every one of `values`.

### Arguments

* `values` is a list of required members.

### Returns

* a `t:t/0`.

### Examples

    iex> Agenda.Predicate.all_of([:projector, :whiteboard]).op
    :all_of

# `any_of`

```elixir
@spec any_of([term()]) :: t()
```

The attribute must be one of `values`.

### Arguments

* `values` is a list of acceptable values.

### Returns

* a `t:t/0`.

### Examples

    iex> Agenda.Predicate.any_of([:sydney, :melbourne]).op
    :any_of

# `at_least`

```elixir
@spec at_least(term()) :: t(:at_least)
```

The attribute must be greater than or equal to `value`.

### Arguments

* `value` is the inclusive lower bound.

### Returns

* a `t:t/0`.

### Examples

    iex> Agenda.Predicate.at_least(8).operand
    8

# `at_most`

```elixir
@spec at_most(term()) :: t(:at_most)
```

The attribute must be less than or equal to `value`.

### Arguments

* `value` is the inclusive upper bound.

### Returns

* a `t:t/0`.

### Examples

    iex> Agenda.Predicate.at_most(4).operand
    4

# `coerce`

```elixir
@spec coerce(t() | term()) :: t()
```

Coerce a bare value into a predicate, leaving predicates untouched.

This is what lets `video_conferencing: true` and
`seats: at_least(8)` sit side by side in the same requirement.

### Arguments

* `value` is a `t:t/0` or any bare term.

### Returns

* a `t:t/0`.

### Examples

    iex> Agenda.Predicate.coerce(true)
    %Agenda.Predicate{op: :exactly, operand: true}

    iex> predicate = Agenda.Predicate.at_least(8)
    iex> Agenda.Predicate.coerce(predicate) == predicate
    true

# `describe`

```elixir
@spec describe(t()) :: String.t()
```

A phrase describing what `predicate` demands, for use in an
explanation.

### Arguments

* `predicate` is a `t:t/0`.

### Returns

* a phrase such as `"at least 8"`, readable inside a sentence.

### Examples

    iex> import Agenda.Predicate
    iex> {describe(at_least(8)), describe(exactly(true))}
    {"at least 8", "true"}

# `exactly`

```elixir
@spec exactly(term()) :: t(:exactly)
```

The attribute must equal `value`. A bare value in a requirement is
sugar for this.

### Arguments

* `value` is the required value.

### Returns

* a `t:t/0`.

### Examples

    iex> Agenda.Predicate.exactly(:sydney).operand
    :sydney

# `none_of`

```elixir
@spec none_of([term()]) :: t()
```

The attribute must be none of `values`.

### Arguments

* `values` is a list of disqualifying values.

### Returns

* a `t:t/0`.

### Examples

    iex> Agenda.Predicate.none_of([:basement]).op
    :none_of

# `satisfied?`

```elixir
@spec satisfied?(t(), term()) :: boolean()
```

`true` when `value` satisfies `predicate`.

A `nil` value — the attribute is absent from the resource — never
satisfies a predicate, except `none_of/1`, which an absent attribute
trivially satisfies.

### Arguments

* `predicate` is a `t:t/0`.

* `value` is the resource's attribute value, or `nil` when absent.

### Returns

* `true` or `false`.

### Examples

    iex> import Agenda.Predicate
    iex> {satisfied?(at_least(8), 8), satisfied?(at_least(8), 4)}
    {true, false}

    iex> import Agenda.Predicate
    iex> satisfied?(at_least(8), nil)
    false

---

*Consult [api-reference.md](api-reference.md) for complete listing*
