View Source Vela behaviour (Vela v1.1.0)

Vela is a tiny library providing easy management of validated cached state with some history.

Including use Vela in your module would turn the module into struct, setting field accordingly to the specification, passed as a parameter.

Vela allows the following configurable parameters per field:

  • limit — length of the series to keep (default: 5)
  • compare_by — comparator extraction function to extract the value, to be used for comparison, from the underlying terms (default: & &1)
  • comparator — the function that accepts a series name and two values and returns the greater one to be used in Vela.δ/1 (default: &</2)
  • threshold — if specified, the inserted value is checked to fit in δ ± threshold; whether it does not, it goes to errors (float() | nil, default: nil)
  • validator — the function to be used to invalidate the accumulated values (default: fn _ -> true end)
  • sorter — the function to be used to sort values within one serie, if none is given, it sorts in the natural order, FIFO, newest is the one to pop
  • corrector — the function to be used to correct the values rejected by validator; the function should return {:ok, corrected_value} to enforce insertion into Vela, or :error if the value cannot be corrected and should be nevertheless rejected
  • errors — number of errors to keep (default: 5)

Also, Vela accepts :__meta__ keyword parameter for the cases when the consumer needs the very custom meta to be passed to the struct.

Vela implements Access behaviour.

Usage

defmodule Vela.Test do
  use Vela,
    series1: [limit: 3, errors: 1], # no validation
    series2: [limit: 2, validator: Vela.Test]
    series3: [
          compare_by: &Vela.Test.comparator/1,
          validator: &Vela.Test.validator/2
    ]

  @behaviour Vela.Validator

  @impl Vela.Validator
  def valid?(_serie, value), do: value > 0

  @spec comparator(%{created_at :: DateTime.t()}) :: DateTime.t()
  def comparator(%{created_at: created_at}),
    do: created_at

  @spec validator(value :: t()) :: boolean()
  def validator(value),
    do: is_integer(value) and value > 300
end

In the example above, before any structure update attempt (via Access,) this valid?/2 function would be called.

If it returns true, the value gets inserted / updated, and the series behind is truncated if needed. It it returns false, the state is not updated, and the value is put into the map under __errors__ key of the struct. The length of errors is also configurable via errors: keyword parameter.

Summary

Types

The type of comparator function to be passed as :comparator keyword parameter to the series.

The type of sorter function to be passed as :sorter keyword parameter to the series.

Configuration options used by the outmost world

Represents a key-value pair in errors and unmatched

Options allowed in series configuration

Series configuration

Represents a key in the Vela structure

The type of sorter function to be passed as :sorter keyword parameter to the series.

Represents the internal state aka per-vela property container

t()

Represents the struct created by this behaviour module

The type of validator function to be passed as :validator keyword parameter to the series.

Represents a value in the Vela structure

Callbacks

Returns a keyword with series as keys and the average value as a value

Returns configuration needed to proceed with the interface Vela exposes.

Returns {min, max} tuple for each serie, using the comparator given as a second parameter, or a default comparator for this serie.

Empties series in Vela given as an input, meta stays untouched.

Checks if Vela given as an input is empty

Checks two velas given as an input for equality

Merges two Velas, using resolver/3 given as the third argument in a case of ambiguity.

Removes obsoleted elements from the series using the validator given as a second parameter, or a default validator for this serie.

Returns series defined by this Vela as a list of atoms.

Returns a keyword with series as keys and the hottest value as a value

Functions

Empties the values for all series of Vela given.

Returns true if the Vela is empty, false otherwise.

Returns true if velas given as arguments are of the same type and series values equal for each serie.

Flat maps the series using fun and returns the keyword with duplicated keys and mapped values.

Maps the series using fun and returns the new Vela instance with series mapped

Merges two Velas given using resolver/3 function.

Inserts the new value into the serie, going through all the validation and sorting.

Slices the Vela given as a first agrument, returning the keyword with series and topmost value as a value.

Returns {min, max} tuple for each serie, using the comparator given as a second parameter, or a default comparator for each serie.

Types

@type comparator() :: (value(), value() -> boolean())

The type of comparator function to be passed as :comparator keyword parameter to the series.

@type corrector() :: (t(), serie(), value() -> {:ok, value()} | :error)

The type of sorter function to be passed as :sorter keyword parameter to the series.

@type exposed_option() ::
  :sorter | :compare_by | :comparator | :corrector | :threshold | :validator

Configuration options used by the outmost world

@type kv() :: {serie(), value()}

Represents a key-value pair in errors and unmatched

@type option() ::
  {:limit, non_neg_integer()}
  | {:type, any()}
  | {:initial, [term()]}
  | {:compare_by, (value() -> any())}
  | {:comparator, comparator()}
  | {:threshold, number()}
  | {:validator, validator()}
  | {:sorter, sorter()}
  | {:corrector, corrector()}
  | {:errors, keyword()}

Options allowed in series configuration

@type options() :: [option()]

Series configuration

@type serie() :: atom()

Represents a key in the Vela structure

@type sorter() :: (value(), value() -> boolean())

The type of sorter function to be passed as :sorter keyword parameter to the series.

@type state() :: Access.t()

Represents the internal state aka per-vela property container

@type t() :: %{
  :__struct__ => atom(),
  :__errors__ => [kv()],
  :__meta__ => state(),
  optional(serie()) => [value()]
}

Represents the struct created by this behaviour module

@type validator() :: (value() -> boolean()) | (serie(), value() -> boolean())

The type of validator function to be passed as :validator keyword parameter to the series.

@type value() :: any()

Represents a value in the Vela structure

Callbacks

Link to this callback

average(vela, averager)

View Source (optional) (since 0.14.0)
@callback average(vela :: t(), averager :: ([value()] -> value()) | module()) :: [kv()]

Returns a keyword with series as keys and the average value as a value

Example:

iex> defmodule XY do
...>   use Vela, x: [], y: []
...> end
...> XY.average(struct(XY, x: [1, 2, 3], y: [5, 7, 9]), &(Enum.sum(&1) / length(&1)))
[x: 2.0, y: 7.0]
...> defmodule Averager do
...>   def average(values), do: Enum.sum(values) / length(values)
...> end
...> XY.average(struct(XY, x: [1, 2, 3], y: [5, 7, 9]), Averager)
[x: 2.0, y: 7.0]
@callback config(serie(), key :: exposed_option(), vela :: t()) :: option()

Returns configuration needed to proceed with the interface Vela exposes.

Example:

iex> defmodule VC do
...>   use Vela, a: [validator: VC], b: [], c: []
...>   @behaviour Vela.Validator
...>   @impl Vela.Validator
...>   def valid?(:a, value), do: not is_nil(value)
...> end
...> VC.config(:a, :comparator, struct(VC, []))
Link to this callback

delta(vela, comparator)

View Source (optional)
@callback delta(vela :: t(), comparator :: nil | (serie(), value(), value() -> boolean())) ::
  [
    {atom(), {value(), value()}}
  ]

Returns {min, max} tuple for each serie, using the comparator given as a second parameter, or a default comparator for this serie.

@callback empty!(vela :: t()) :: t()

Empties series in Vela given as an input, meta stays untouched.

@callback empty?(vela :: t()) :: boolean()

Checks if Vela given as an input is empty

Link to this callback

equal?(vela1, vela2)

View Source (optional)
@callback equal?(vela1 :: t(), vela2 :: t()) :: boolean()

Checks two velas given as an input for equality

@callback merge(t(), t(), (serie(), value(), value() -> value())) :: t()

Merges two Velas, using resolver/3 given as the third argument in a case of ambiguity.

Used by: Vela.merge/3.

Link to this callback

purge(vela, validator)

View Source (optional)
@callback purge(vela :: t(), validator :: nil | validator()) :: t()

Removes obsoleted elements from the series using the validator given as a second parameter, or a default validator for this serie.

@callback series() :: [serie()]

Returns series defined by this Vela as a list of atoms.

Example:

iex> defmodule V do
...>   use Vela, a: [], b: [], c: []
...> end
...> V.series()
[:a, :b, :c]
@callback slice(vela :: t()) :: [kv()]

Returns a keyword with series as keys and the hottest value as a value

Example:

iex> defmodule AB do
...>   use Vela, a: [], b: [], c: []
...> end
...> AB.slice(struct(AB, [a: [1, 2], b: [3], c: []]))
[a: 1, b: 3]

Functions

@spec empty!(t()) :: t()

Empties the values for all series of Vela given.

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

Returns true if the Vela is empty, false otherwise.

Link to this function

equal?(v1, v2)

View Source (since 0.9.2)
@spec equal?(v1 :: t(), v2 :: t()) :: boolean()

Returns true if velas given as arguments are of the same type and series values equal for each serie.

This function does not check internal state, only the values.

Link to this function

flat_map(vela, fun \\ & &1)

View Source
@spec flat_map(vela :: t(), (kv() -> kv()) | (serie(), value() -> kv())) :: [kv()]

Flat maps the series using fun and returns the keyword with duplicated keys and mapped values.

Example:

defmodule EO do
  use Vela,
    even: [limit: 2],
    odd: [limit: 2]

  def flat_map(%EO{} = v),
    do: Vela.flat_map(v, & {&1, &2+1})
end
EO.flat_map(struct(EO, [even: [2, 4], odd: [1, 3]]))

#⇒ [even: 3, even: 5, odd: 2, odd: 4]
@spec map(
  vela :: t(),
  (kv() -> value())
  | (serie(), value() -> value())
  | ({serie(), value()} -> {serie(), value()})
) :: t()

Maps the series using fun and returns the new Vela instance with series mapped

@spec merge(t(), t(), (serie(), value(), value() -> value())) :: t()

Merges two Velas given using resolver/3 function.

This function does not allow merging states, the first argument wins. To update state, use update_state/2.

@spec put(vela :: t(), serie :: serie(), value :: value()) :: t()

Inserts the new value into the serie, going through all the validation and sorting.

If the value has not passed validation, it’s put into :__errors__ internal list. If the new length of the serie exceeds the limit set for this serie, the last value (after sorting) gets discarded.

Link to this function

slice(vela, slicer \\ :slice)

View Source
@spec slice(
  t(),
  :slice
  | {:average, ([value()] -> value()) | module()}
  | ({serie(), value()} -> value())
) :: [{serie(), value()}]

Slices the Vela given as a first agrument, returning the keyword with series and topmost value as a value.

The second argument might be:

  • {:average, averager} to return an averaged value, assuming Vela.average/2 callback is defined,
  • :slice to return the sliced value in the list of values, assuming Vela.slice/1 is defined,
  • ({serie, values} -> value) function to perform slicing ad-hoc
Link to this function

δ(vela, comparator \\ nil)

View Source (since 0.7.0)
@spec δ(t(), nil | (serie(), value(), value() -> boolean())) :: [
  {atom(), {value(), value()}}
]

Returns {min, max} tuple for each serie, using the comparator given as a second parameter, or a default comparator for each serie.