Brex v0.2.3 Brex View Source

A Specification Pattern implementation in Elixir.

Using brex you can easily

  • define
  • compose and
  • evaluate

business rules to dynamically drive the flow of your application.

Basics

The lowest building stone of Brex is a rule. A rule can have many shapes, for example this is a rule:

&is_list/1

This is a rule too:

Brex.all([&is_list/1, &(length(&1) > 0)])

Or this:

defmodule MyRule do
  def evaluate(:foo), do: true
  def evaluate(:bar), do: false
end

Also this:

defmodule MyStruct do
  use Brex.Rule.Struct

  defstruct [:foo]

  def evaluate(%{foo: foo}, value) do
    foo == value
  end
end

Enough talk about defining rules, how can I evaluate them?

Well great that you ask, that’s simple too!

Brex.satisfies? MyRule, :foo # => true

As you can see, Brex is flexible and easy to use. All of this is based on the Brex.Rule.Evaluable protocol, if you’re really interested, take a look at Brex.Rule which talks about the possible rule types a little bit more.

Operators

Also, as you might have noticed, I used an all/1 function in the examples above. It’s called a Brex.Operator and represents the compose part of Brex: it allows you to link rules using boolean logic.

It currently supports:

I think the names speak for themself.

More …

Apart from that, this module mainly serves as a utility belt for dealing with rules. It offers some functions to evaluate some rules, or to check if a given value satisfies some rules.

But for this I would suggest to simply take a look at the functions in detail.

I hope you enjoy using Brex!

Link to this section Summary

Functions

Links the given rules in a boolean fashion, similar to the Enum functions

Shortcut for Brex.all([rule1, rule2])

Links the given rules in a boolean fashion, similar to the Enum functions

Shortcut for Brex.any([rule1, rule2])

Evaluates a given rule for a given value. Returns a Brex.Result struct which contains the evaluated rules, the value and - of course - the evaluation result

Links the given rules in a boolean fashion, similar to the Enum functions

Shortcut for Brex.none([rule1, rule2])

Returns the number of “clauses” for the given rule. This is mostly interesting when using operators and having to decide between some matching rules based on how specific they are

Allows to reduce one or many Brex.Result to a simple true or false boolean

Evaluates a rule for a given value and returns a boolean whether or not it satisfies the rule. Equivalent to a result/2 followed by a passed?/1 call

Link to this section Types

Link to this section Functions

Links the given rules in a boolean fashion, similar to the Enum functions.

  • all rules have to pass (and / &&)
  • any one rule is sufficient to pass (or / ||)
  • none of the rules may pass (not / !)

Examples

iex> Brex.all &is_list/1, &is_map/1
%Brex.Operator{
  aggregator: &Brex.Operator.Aggregator.all?/1,
  clauses: [&:erlang.is_list/1, &:erlang.is_map/1]
}

iex> Brex.all [&is_list/1, &is_map/1, &is_binary/1]
%Brex.Operator{
  aggregator: &Brex.Operator.Aggregator.all?/1,
  clauses: [&:erlang.is_list/1, &:erlang.is_map/1, &:erlang.is_binary/1]
}

Shortcut for Brex.all([rule1, rule2]).

Links the given rules in a boolean fashion, similar to the Enum functions.

  • all rules have to pass (and / &&)
  • any one rule is sufficient to pass (or / ||)
  • none of the rules may pass (not / !)

Examples

iex> Brex.any &is_list/1, &is_map/1
%Brex.Operator{
  aggregator: &Brex.Operator.Aggregator.any?/1,
  clauses: [&:erlang.is_list/1, &:erlang.is_map/1]
}

iex> Brex.any [&is_list/1, &is_map/1, &is_binary/1]
%Brex.Operator{
  aggregator: &Brex.Operator.Aggregator.any?/1,
  clauses: [&:erlang.is_list/1, &:erlang.is_map/1, &:erlang.is_binary/1]
}

Shortcut for Brex.any([rule1, rule2]).

Link to this function evaluate(rules, value) View Source
evaluate(one_or_many_rules(), value()) :: result()

Evaluates a given rule for a given value. Returns a Brex.Result struct which contains the evaluated rules, the value and - of course - the evaluation result.

Allows you to pass a list of rules which get linked calling all/1.

Examples

iex> rule = &(length(&1) > 0)
iex> result = Brex.evaluate(rule, [])
iex> match? %Brex.Result{
...>   evaluation: false,
...>   rule: _,
...>   value: []
...> }, result
true

iex> rules = [&is_list/1, &Keyword.keyword?/1]
iex> result = Brex.evaluate(rules, [])
iex> match? %Brex.Result{
...>   evaluation: {:ok, [%Brex.Result{evaluation: true}, %Brex.Result{}]},
...>   rule: %Brex.Operator{clauses: _},
...>   value: []
...> }, result
true

Links the given rules in a boolean fashion, similar to the Enum functions.

  • all rules have to pass (and / &&)
  • any one rule is sufficient to pass (or / ||)
  • none of the rules may pass (not / !)

Examples

iex> Brex.none &is_list/1, &is_map/1
%Brex.Operator{
  aggregator: &Brex.Operator.Aggregator.none?/1,
  clauses: [&:erlang.is_list/1, &:erlang.is_map/1]
}

iex> Brex.none [&is_list/1, &is_map/1, &is_binary/1]
%Brex.Operator{
  aggregator: &Brex.Operator.Aggregator.none?/1,
  clauses: [&:erlang.is_list/1, &:erlang.is_map/1, &:erlang.is_binary/1]
}
Link to this function none(rule1, rule2) View Source
none(rule1 :: Brex.Types.rule(), rule2 :: Brex.Types.rule()) ::
  Brex.Operator.t()

Shortcut for Brex.none([rule1, rule2]).

Link to this function number_of_clauses(rule) View Source
number_of_clauses(Brex.Types.rule()) :: non_neg_integer()

Returns the number of “clauses” for the given rule. This is mostly interesting when using operators and having to decide between some matching rules based on how specific they are.

Examples

iex> Brex.number_of_clauses &is_list/1
1

iex Brex.number_of_clauses Brex.none([&is_list/1, &is_map/1, &is_binary/1])
3

Allows to reduce one or many Brex.Result to a simple true or false boolean.

Examples

iex> Brex.passed? %Brex.Result{evaluation: true}
true

iex> Brex.passed? %Brex.Result{evaluation: :ok}
true

iex> Brex.passed? %Brex.Result{evaluation: {:ok, :success}}
true

iex> Brex.passed? %Brex.Result{evaluation: false}
false

iex> Brex.passed? %Brex.Result{evaluation: :error}
false

iex> Brex.passed? %Brex.Result{evaluation: {:error, :failure}}
false

iex> Brex.passed?  %Brex.Result{evaluation: :unknown_evaluation}
** (FunctionClauseError) no function clause matching in Brex.Result.passed?/1
Link to this function satisfies?(rules, value) View Source
satisfies?(one_or_many_rules(), value()) :: boolean()

Evaluates a rule for a given value and returns a boolean whether or not it satisfies the rule. Equivalent to a result/2 followed by a passed?/1 call.

Allows you to pass a list of rules which get linked calling all/1.

Examples

iex> Brex.satisfies? &is_list/1, []
true

iex> Brex.satisfies? Brex.any(&is_list/1, &is_map/1), []
true

iex> Brex.satisfies? Brex.any(&is_list/1, &is_map/1), %{}
true

iex> Brex.satisfies? Brex.any(&is_list/1, &is_map/1), ""
false