View Source BrimstoneConditional (brimstone_conditional v0.1.1)

Evaluate conditions defined in a logical structure.

This module allows to evaluate complex tree conditional structures and digest them to get a final output. The main function is evaluate/2 which will digest the struct and return the values with the conditional switches resolved.

The struct itself can be converted to a string to be stored, using :erlang.term_to_binary/1 and Base.encode64/1 under the hood. The struct can be retrieved later using from_string/1, which performs the mirror operation. These strings include a version at the begining to acomodate the possibility of altering this struct in the future and allowing migrations from previous stringified conditionals.

Link to this section Summary

Functions

Evaluate conditions defined in a condition structure, returning the computed value.

Restore a conditional struct from its string form.

Link to this section Functions

Link to this function

evaluate(condition, state \\ %{})

View Source

Evaluate conditions defined in a condition structure, returning the computed value.

The struct uses recursion to evaluate its parameters. A plain boolean will return itself, a list will be assumed to be an and structure, and a map or keyword list will traverse itself as a list of key/value tuples, using the key as the operation and the value as parameters.

Known map operators are the logic gates and, or, xor, not, nor and xnor, the comparison operators eq, neq, gt, ge, lt and le, the check operators in and match, the disambiguator cond, the scape hatch fn, and the utility operators var, count, cat, interpolate, each and sum.

The comparison operators assume that the first element is the topic that we are comparing, and any other element is what are we comparing it to, therefore is possible to ask if something is greather that "this thing" and also to "that other thing" in the same step, or if "this thing" if different to "this other thing" and also to "that other thing".

The check operator match will check if the string provided as the first element of the argument list matches all regex and strings provided as the rest of arguments. Strings will be compiled to regex, and other data types will try to convert themselves to a string, then surround themselves with the regex operators ^ and $ and compile the resulting string to a regex, thus if you try to match "22" and "2" this will return true, as "2" is a string contained in "22", but if you try to match "22" and 2 it will return false, as the integer 2 will become the regex "^2$". This is intentional. If you require to pass aditional options to the regex you wish to compile, you may do so with a tuple of size two where each element correspond to the arguments of Regex.compile!/2.

The check operator in will test membership of elements on the first element provided. It works on Lists, Strings and Maps. Maps are a special case, as it will check if the specified map arguments are a subset of the topic map.

The disambiguator operator cond will operate exactly as an elixir cond. It expects to receive a list of tuples, where each tuple is a pair with a condition that will be evaluated with this very same function, and a value. It will substitute itself with the first element of this list that return true after checking its condition.

The utility operators will perform common basic tasks, usually on the state provided (an empty map as default) to fetch data. All these tasks could be handled as functions, but they are so common that including them make the struct way more usable.

var will perform a Map.get/2 using the provided atom or string as key.

each will turn itself into a list containing all values of the state map which had a key begining with the atom or string provided, followed with an index inside brackets (like the accessor syntax).

count will return the size of the provided list.

sum will asume the provided list contains numbers and will add them up.

cat will try to catenate the elements of the provided argument list following a DWIM approach. To do so it will run a reduce function on the list and will take into account the data types of both the accumulator that is being built and the element we are trying to add. I.E: catenating two strings, two lists or two tuples together will simply join them, catenating a keyword list to a map will add the keywords to the map, but catenating a map to a list (keyword or not) will simply append the map as the last element of the list, and so on. If there is not a known approach to what we are trying to catenate, this operator will try to cast both elements to string before joining them.

interpolate expects a list where the first element is a string and the rest are interpolation arguments. Interpolation arguments may be key-value pairs or simple values. The string will be reduced searching for each element in the argument list following gettext conventions. If the pattern %{keyname} is fount, it will be replaced by value. For values that do not have a key, the reduce function will match the ordinal (starting at one) (I.E: %{1}, %{2}, etc.) of a list composed of all arguments that are not key-value pairs.

Any other operator that receives a list as a parameter will be handled as an and operation of the result of applying the specfied operator to each element in the list, with the exception of the scape hatch fn, which will assume that the first element of the provided list is a function and will apply using the rest of the list as arguments. The fn operator also can acceps a different syntax using tuples, where you may specify {module, function_name, arg_list} or {function, arg_list}. In any case, it will check the arity of the relevant function. If the arity is equal to the arguments provided it will call it only with the provided arguments, and prepend the entire state to the argument list otherwise.

Restore a conditional struct from its string form.