PhoenixFlags.Target (PhoenixFlags v0.9.0)

Copy Markdown View Source

A targeting rule: force a flag's value when the request context matches.

This is what lets you turn a feature on for one customer without a deploy. A rule belongs to a flag, carries an ordered list of conditions that must all match, and a value to return when they do.

MyApp.SystemConfig.put_target("enable_benefits",
  conditions: [[attribute: :company_id, operator: :in, values: [123, 456]]],
  value: "true"
)

Rules are evaluated in position order and the first match wins. A matching rule overrides both the stored value and, for a :variant flag, the weighted split — "force this for them" would not mean much otherwise.

Operators

OperatorMatches when
:inthe context value equals any of values
:not_inthe context value equals none of values
:eqthe context value equals the first of values
:starts_withthe context value starts with any of values

Comparison is by string

Every flag value in PhoenixFlags is stored as a string, and rule values follow suit. Both sides of a comparison are put through to_string/1, so a context of %{company_id: 123} matches a rule value of "123". Attribute keys are compared the same way, so :company_id and "company_id" are one attribute.

If a rule never seems to fire, this is almost always why — check that the value you expect is what to_string/1 produces (a Decimal, a struct, or a float will not stringify the way you might assume).

A missing attribute never matches

A rule on company_id does not match a context without a company_id. It does not match vacuously and it does not raise — an absent attribute simply is not a match, so :not_in on a missing attribute is false too.

Summary

Functions

Changeset for a targeting rule and its conditions.

Whether every condition on target matches context.

The operators a condition may use.

Returns the value of the first rule whose conditions all match context, or :none.

Types

t()

@type t() :: %PhoenixFlags.Target{
  __meta__: term(),
  conditions: term(),
  id: term(),
  inserted_at: term(),
  key: term(),
  position: term(),
  updated_at: term(),
  value: term()
}

Functions

changeset(target, attrs)

Changeset for a targeting rule and its conditions.

Validates the shape only. Whether value is legal for the flag's type is checked by the caller, which is the only place that knows the declaration — see PhoenixFlags.Server.put_target/3.

matches?(target, context)

@spec matches?(t(), map()) :: boolean()

Whether every condition on target matches context.

A rule with no conditions never matches — an empty condition list would otherwise force its value on everyone, which is never what was meant.

context may be a plain map; keys and values are normalised the same way resolve/2 does.

operators()

@spec operators() :: [atom()]

The operators a condition may use.

resolve(targets, context)

@spec resolve([t()], map()) :: {:ok, String.t()} | :none

Returns the value of the first rule whose conditions all match context, or :none.

Rules are assumed to arrive in position order — PhoenixFlags.Server sorts them once when the cache loads rather than on every read.

Never raises: a malformed rule or context yields no match rather than taking down the caller, because this sits on the read path.