Bandera.Gate (bandera v0.1.0)

Copy Markdown

A single feature-flag gate and its evaluation.

Summary

Functions

Returns true if the gate is an :actor gate.

Returns true if the gate is a :boolean gate.

Evaluates a single gate against options.

Returns true if the gate is a :group gate.

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

Builds a gate of the given type.

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

Returns true if the gate is a :percentage_of_actors gate.

Returns true if the gate is a :percentage_of_time gate.

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

Types

t()

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

Functions

actor?(gate)

@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?(gate)

@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?(gate, options \\ [])

@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?(gate)

@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(gate)

@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(type, enabled)

@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(atom, actor, enabled)

@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?(gate)

@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?(gate)

@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(actor, flag_name)

@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