Bandera.Gate (bandera v0.4.0)

Copy Markdown View Source

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.

Returns true if the gate is a :prerequisite gate.

Returns true if the gate is a :rule gate.

Returns true if the gate is a :schedule gate.

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

Returns true if the gate is a :segment gate.

Returns true if the gate is a :variant gate.

Types

t()

@type t() :: %Bandera.Gate{
  enabled: boolean(),
  for: term(),
  type:
    :boolean
    | :actor
    | :group
    | :percentage_of_time
    | :percentage_of_actors
    | :variant
    | :rule
    | :segment
    | :prerequisite
    | :schedule,
  value: term()
}

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()
@spec new(:variant, %{optional(String.t()) => number()}) :: t()
@spec new(
  :schedule,
  {String.t() | nil, String.t() | nil}
) :: t()

Builds a gate of the given type.

The two-argument form covers :boolean gates (with a boolean value), the two percentage gate types (:percentage_of_time / :percentage_of_actors, with a ratio strictly between 0.0 and 1.0), :variant gates (with a %{name => weight} map), and :schedule gates (with a {from, until} tuple of ISO-8601 strings or nil bounds). The three-argument form (see below) covers :actor, :group, :rule, :segment, and :prerequisite gates.

Raises Bandera.Gate.InvalidTargetError when a percentage ratio is out of range, a variant weights map is empty, all variant weights are zero, or any variant weight is negative.

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()
@spec new(:rule, [Bandera.Constraint.t()], boolean()) :: t()
@spec new(:segment, atom() | String.t(), boolean()) :: t()
@spec new(:prerequisite, atom(), 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

prerequisite?(gate)

@spec prerequisite?(t()) :: boolean()

Returns true if the gate is a :prerequisite gate.

Examples

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

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

rule?(gate)

@spec rule?(t()) :: boolean()

Returns true if the gate is a :rule gate.

Examples

iex> Bandera.Gate.rule?(Bandera.Gate.new(:rule, [], true))
true

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

schedule?(gate)

@spec schedule?(t()) :: boolean()

Returns true if the gate is a :schedule gate.

Examples

iex> Bandera.Gate.schedule?(Bandera.Gate.new(:schedule, {nil, nil}))
true

iex> Bandera.Gate.schedule?(Bandera.Gate.new(:boolean, true))
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

segment?(gate)

@spec segment?(t()) :: boolean()

Returns true if the gate is a :segment gate.

Examples

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

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

variant?(gate)

@spec variant?(t()) :: boolean()

Returns true if the gate is a :variant gate.

Examples

iex> Bandera.Gate.variant?(Bandera.Gate.new(:variant, %{"a" => 1}))
true

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