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 inVela.δ/1(default:&</2)threshold— if specified, the inserted value is checked to fit inδ ± threshold; whether it does not, it goes toerrors(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 topopcorrector— the function to be used to correct the values rejected byvalidator; the function should return{:ok, corrected_value}to enforce insertion intoVela, or:errorif the value cannot be corrected and should be nevertheless rejectederrors— 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
endIn 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
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 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
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.
@type exposed_option() ::
  :sorter | :compare_by | :comparator | :corrector | :threshold | :validator
      Configuration options used by the outmost world
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
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
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
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, []))
  @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.
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.
Used by: Vela.merge/3.
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]
  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
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.
This function does not check internal state, only the values.
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
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.
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.
@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, assumingVela.average/2callback is defined,:sliceto return the sliced value in the list of values, assumingVela.slice/1is defined,({serie, values} -> value)function to perform slicing ad-hoc
Returns {min, max} tuple for each serie, using the comparator given as a second parameter,
  or a default comparator for each serie.