# `Bandera.Gate`

A single feature-flag gate and its evaluation.

# `t`

```elixir
@type t() :: %Bandera.Gate{
  enabled: boolean(),
  for: term(),
  type: :boolean | :actor | :group | :percentage_of_time | :percentage_of_actors
}
```

# `actor?`

```elixir
@spec actor?(t()) :: boolean()
```

Returns `true` if the gate is an `:actor` gate.

## Examples

    iex> Bandera.Gate.actor?(Bandera.Gate.new(:actor, "u1", true))
    true

    iex> Bandera.Gate.actor?(Bandera.Gate.new(:boolean, true))
    false

# `boolean?`

```elixir
@spec boolean?(t()) :: boolean()
```

Returns `true` if the gate is a `:boolean` gate.

## Examples

    iex> Bandera.Gate.boolean?(Bandera.Gate.new(:boolean, true))
    true

    iex> Bandera.Gate.boolean?(Bandera.Gate.new(:actor, "u1", true))
    false

# `enabled?`

```elixir
@spec enabled?(
  t(),
  keyword()
) :: {:ok, boolean()} | :ignore
```

Evaluates a single gate against `options`.

Returns `{:ok, boolean}` when this gate decides the outcome, or `:ignore` when it
does not apply to the given input (so `Bandera.Flag` can fall through to the next
gate). Boolean and percentage-of-time gates always decide; actor and group gates
return `:ignore` unless `options[:for]` matches their target; percentage-of-actors
gates require both `:for` and `:flag_name`.

## Examples

    iex> Bandera.Gate.enabled?(Bandera.Gate.new(:boolean, true))
    {:ok, true}

    iex> Bandera.Gate.enabled?(Bandera.Gate.new(:actor, "u1", true), for: "u1")
    {:ok, true}

    iex> Bandera.Gate.enabled?(Bandera.Gate.new(:actor, "u1", true), for: "u2")
    :ignore

# `group?`

```elixir
@spec group?(t()) :: boolean()
```

Returns `true` if the gate is a `:group` gate.

## Examples

    iex> Bandera.Gate.group?(Bandera.Gate.new(:group, :beta, true))
    true

    iex> Bandera.Gate.group?(Bandera.Gate.new(:boolean, true))
    false

# `id`

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

Returns the gate's storage id, used as the per-flag slot key.

Both percentage gate types collapse to `"percentage"` (a flag holds at most one
percentage gate), while actor and group ids embed their target.

## Examples

    iex> Bandera.Gate.id(Bandera.Gate.new(:boolean, true))
    "boolean"

    iex> Bandera.Gate.id(Bandera.Gate.new(:actor, "u1", true))
    "actor/u1"

    iex> Bandera.Gate.id(Bandera.Gate.new(:percentage_of_actors, 0.25))
    "percentage"

# `new`

```elixir
@spec new(:boolean, boolean()) :: t()
@spec new(:percentage_of_time | :percentage_of_actors, float()) :: t()
```

Builds a gate of the given `type`.

The two-argument form covers `:boolean` gates (with a boolean value) and the two
percentage gate types (`:percentage_of_time` / `:percentage_of_actors`, with a
ratio strictly between `0.0` and `1.0`). The three-argument form (see below)
covers `:actor` and `:group` gates.

Raises `Bandera.Gate.InvalidTargetError` when a percentage ratio is out of range.

## Examples

    iex> Bandera.Gate.new(:boolean, true)
    %Bandera.Gate{type: :boolean, for: nil, enabled: true}

    iex> Bandera.Gate.new(:percentage_of_actors, 0.25)
    %Bandera.Gate{type: :percentage_of_actors, for: 0.25, enabled: true}

    iex> Bandera.Gate.new(:percentage_of_time, 1.5)
    ** (Bandera.Gate.InvalidTargetError) percentage_of_time gates require a ratio in the range 0.0 < r < 1.0

# `new`

```elixir
@spec new(:actor, term(), boolean()) :: t()
@spec new(:group, atom() | String.t(), boolean()) :: t()
```

Builds an `:actor` or `:group` gate targeting `for` with the boolean `enabled`.

Actor targets are normalised to a string id via the `Bandera.Actor` protocol;
group names are stringified.

## Examples

    iex> Bandera.Gate.new(:actor, "user-1", true)
    %Bandera.Gate{type: :actor, for: "user-1", enabled: true}

    iex> Bandera.Gate.new(:group, :beta, false)
    %Bandera.Gate{type: :group, for: "beta", enabled: false}

# `percentage_of_actors?`

```elixir
@spec percentage_of_actors?(t()) :: boolean()
```

Returns `true` if the gate is a `:percentage_of_actors` gate.

## Examples

    iex> Bandera.Gate.percentage_of_actors?(Bandera.Gate.new(:percentage_of_actors, 0.5))
    true

    iex> Bandera.Gate.percentage_of_actors?(Bandera.Gate.new(:percentage_of_time, 0.5))
    false

# `percentage_of_time?`

```elixir
@spec percentage_of_time?(t()) :: boolean()
```

Returns `true` if the gate is a `:percentage_of_time` gate.

## Examples

    iex> Bandera.Gate.percentage_of_time?(Bandera.Gate.new(:percentage_of_time, 0.5))
    true

    iex> Bandera.Gate.percentage_of_time?(Bandera.Gate.new(:percentage_of_actors, 0.5))
    false

# `score`

```elixir
@spec score(term(), atom()) :: float()
```

Deterministic score in `[0.0, 1.0)` for an actor + flag pair (first 16 bits of
SHA-256).

The pairing of actor and flag name means an actor lands at a different point in
every flag's rollout, and the result is stable across nodes and restarts — the
basis for sticky `percentage_of_actors` gates.

## Examples

    iex> Bandera.Gate.score("user-42", :my_flag)
    0.9317474365234375

    iex> Bandera.Gate.score("user-42", :my_flag) == Bandera.Gate.score("user-42", :my_flag)
    true

---

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